<commit_before>// ------------------------------------------------------------------------
// Pion is a development platform for building Reactors that process Events
// ------------------------------------------------------------------------
// Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
//
// Pion 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.
//
// Pion 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 Pion.  If not, see <http://www.gnu.org/licenses/>.
//

#ifndef __PION_DATABASE_HEADER__
#define __PION_DATABASE_HEADER__

#include <map>
#include <string>
#include <pion/PionConfig.hpp>
#include <pion/platform/Event.hpp>
#include <pion/platform/Vocabulary.hpp>
#include <pion/platform/PlatformPlugin.hpp>
#include <pion/platform/Query.hpp>

namespace pion {		// begin namespace pion
namespace platform {	// begin namespace platform (Pion Platform Library)

///
/// Database: abstract class for storing and retrieving Events
///
class PION_PLATFORM_API Database
	: public PlatformPlugin
{
public:

	/// exception thrown if the database is busy & the query should be tried again later
	class DatabaseBusyException : public std::exception {
	public:
		virtual const char* what() const throw() {
			return "Database is busy.  Try again later";
		}
	};

	/// exception thrown if a Query cannot be found
	class QueryNotFoundException : public PionException {
	public:
		QueryNotFoundException(const std::string& query_id)
			: PionException("Unable find database query identified by: ", query_id) {}
	};

	/// exception thrown if there is an error opening a Database
	class OpenDatabaseException : public PionException {
	public:
		OpenDatabaseException(const std::string& db_name)
			: PionException("Unable to open database: ", db_name) {}
	};

	/// exception for missing configuration element
	class DatabaseConfigMissing: public PionException {
	public:
		DatabaseConfigMissing(const std::string& database_id)
			: PionException("Database template element missing: ", database_id) {}
	};

	class DatabaseClientException : public PionException {
	public:
		DatabaseClientException(const std::string& database_id)
			: PionException("Database client not defined: ", database_id) {}
	};

	/// Isolation level must be: ReadUncommitted (default), ReadCommitted, RepeatableRead, Serializable
	class InvalidIsolationLevel : public PionException {
	public:
		InvalidIsolationLevel(const std::string& database_id)
			: PionException("Invalid Isolation level defined: ", database_id) {}
	};

	/// Bad type pair in Vocabulary-to-SQL map
	class BadTypePair : public PionException {
	public:
		BadTypePair(const std::string& bad_pair)
			: PionException("Bad SQL Type pair: ", bad_pair) {}
	};

	/// Every database engine must have Vocabulary-to-SQL map
	class MissingTypeMap : public PionException {
	public:
		MissingTypeMap(const std::string& database_id)
			: PionException("SQL Type map missing: ", database_id) {}
	};

	class MissingTemplateException : public PionException {
	public:
		MissingTemplateException(const std::string& database_id)
			: PionException("Database template file not found: ", database_id) {}
	};

	class MissingRootElementException : public PionException {
	public:
		MissingRootElementException(const std::string& database_id)
			: PionException("Database template file missing root element: ", database_id) {}
	};

	class ReadConfigException: public PionException {
	public:
		ReadConfigException(const std::string& database_id)
			: PionException("Database template file not readable: ", database_id) {}
	};

	/// constructs a new Database object
	Database(void)
		: m_isolation_level(IL_LevelUnknown)
	{}

	/// virtual destructor: this class is meant to be extended
	virtual ~Database() {}

	/**
	 * retrieves a pre-compiled query for the database
	 *
	 * @param query_id string used to uniquely identify the type of query
	 * @return QueryPtr pointer to the compiled query
	 */
	inline QueryPtr getQuery(QueryID query_id) const {
		QueryMap::const_iterator i = m_query_map.find(query_id);
		if (i == m_query_map.end())
			throw QueryNotFoundException(query_id);
		return i->second;
	}

	/**
	 * clones the Database, returning a pointer to the cloned copy
	 *
	 * @return DatabasePtr pointer to the cloned copy of the Database
	 */
	virtual boost::shared_ptr<Database> clone(void) const = 0;

	/**
	 * opens the database connection
	 *
	 * @param create_backup if true, create a backup of the old database before opening
	 */
	virtual void open(bool create_backup = false) = 0;

	/// closes the database connection
	virtual void close(void) = 0;

	/// returns true if the database connection is open
	virtual bool is_open(void) const = 0;

	/**
	 * runs a simple query, ignoring any results returned
	 *
	 * @param sql_query SQL query to execute
	 */
	virtual void runQuery(const std::string& sql_query) = 0;

	/**
	 * adds a compiled SQL query to the database
	 *
	 * @param query_id string used to uniquely identify the type of query
	 * @param sql_query SQL query to compile and cache for later use
	 */
	virtual QueryPtr addQuery(QueryID query_id, const std::string& sql_query) = 0;

	/**
	 * creates a database table for output, if it does not already exist
	 *
	 * @param field_map mapping of Vocabulary Terms to Database fields
	 * @param table_name name of the table to create
	 */
	virtual void createTable(const Query::FieldMap& field_map,
							 const std::string& table_name) = 0;

	/**
	 * prepares the query that is used to insert events
	 *
	 * @param field_map mapping of Vocabulary Terms to Database fields
	 * @param table_name name of the table to insert events into
	 *
	 * @return QueryPtr smart pointer to the new query for inserting events
	 */
	virtual QueryPtr prepareInsertQuery(const Query::FieldMap& field_map,
										const std::string& table_name) = 0;

	/**
	 * returns the query that is used to begin new transactions
	 *
	 * @return QueryPtr smart pointer to the "begin transaction" query
	 */
	virtual QueryPtr getBeginTransactionQuery(void) = 0;

	/**
	 * returns the query that is used to end and commit transactions
	 *
	 * @return QueryPtr smart pointer to the "commit transaction" query
	 */
	virtual QueryPtr getCommitTransactionQuery(void) = 0;

	/**
	 * sets configuration parameters for this Database
	 *
	 * @param v the Vocabulary that this Database will use to describe Terms
	 * @param config_ptr pointer to a list of XML nodes containing Database
	 *                   configuration parameters
	 */
	virtual void setConfig(const Vocabulary& v, const xmlNodePtr config_ptr);

	void stringReplace(std::string& src, const char* search, const std::string& substitute);

	std::string& stringSubstitutes(std::string& query, const pion::platform::Query::FieldMap& field_map, const std::string& table_name);

	/// Database Isolation Levels
	enum IsolationLevel_t {
		IL_LevelUnknown = -1,
		IL_ReadUncommitted,
		IL_ReadCommitted,
		IL_RepeatableRead,
		IL_Serializable
	};

protected:

	/// protected copy function (use clone() instead)
	inline void copyDatabase(const Database& d) {
		PlatformPlugin::copyPlugin(d);
		// clone all the top level defined member variables
		m_database_engine = d.m_database_engine;
		m_database_client = d.m_database_client;
		m_begin_insert = d.m_begin_insert;
		m_commit_insert = d.m_commit_insert;
		m_create_log = d.m_create_log;
		m_insert_log = d.m_insert_log;
		m_create_stat = d.m_create_stat;
		m_update_stat = d.m_update_stat;
		m_select_stat = d.m_select_stat;
		m_isolation_level = d.m_isolation_level;
		// Query_map will be overridden
//		m_query_map = d.m_query_map;
		m_sql_affinity = d.m_sql_affinity;
	}

	/// data type that maps query identifiers to pointers of compiled queries
	typedef std::map<QueryID, QueryPtr>		QueryMap;

	/// unique identifier used to represent the "insert event" query
	static const std::string				INSERT_QUERY_ID;

	/// unique identifier used to represent the "begin transaction" query
	static const std::string				BEGIN_QUERY_ID;

	/// unique identifier used to represent the "commit transaction" query
	static const std::string				COMMIT_QUERY_ID;

	static const std::string				TEMPLATE_FILE;
	static const std::string				ROOT_ELEMENT_NAME;
	static const std::string				TEMPLATE_ELEMENT_NAME;
	static const std::string				MAP_ELEMENT_NAME;
	static const std::string				PAIR_ELEMENT_NAME;

	static const std::string				ENGINE_ELEMENT_NAME;

	static const std::string				CLIENT_ELEMENT_NAME;
	static const std::string				BEGIN_ELEMENT_NAME;
	static const std::string				COMMIT_ELEMENT_NAME;
	static const std::string				CREATE_LOG_ELEMENT_NAME;
	static const std::string				INSERT_LOG_ELEMENT_NAME;
	static const std::string				ISOLATION_ELEMENT_NAME;

	static const std::string				CREATE_STAT_ELEMENT_NAME;
	static const std::string				UPDATE_STAT_ELEMENT_NAME;
	static const std::string				SELECT_STAT_ELEMENT_NAME;

	void readConfig(const xmlNodePtr config_ptr, std::string engine_str);
	void readConfigDetails(const xmlNodePtr config_ptr);

	/// Database engine name, e.g. MySQL-MyISAM
	/// This is used to find a matching set of SQL from configuation file
	std::string								m_database_engine;

	/// name of database client, e.g. MySQL
	std::string								m_database_client;

	/// begin & commit insert strings
	std::string								m_begin_insert;
	std::string								m_commit_insert;
	std::string								m_create_log;
	std::string								m_insert_log;

	std::string								m_create_stat;
	std::string								m_update_stat;
	std::string								m_select_stat;

	/// Isolation level, default is SA_ReadUncommitted (for speed/ease)
	IsolationLevel_t						m_isolation_level;

	/// used to keep track of all the database's pre-compiled queries
	QueryMap								m_query_map;

	/// Affinity map; index is term_type, value should be compatible SQL type
	std::vector<std::string>				m_sql_affinity;
};

/// data type used for Database smart pointers
typedef boost::shared_ptr<Database>			DatabasePtr;

//
// The following symbols must be defined for any Databases that you would
// like to be able to load dynamically using the DatabaseManager::load()
// function.  These are not required for any Databases that you only want
// to link directly into your programs.
//
// Make sure that you replace "DATABASE" with the name of your derived class.
// This name must also match the name of the object file (excluding the
// extension).  These symbols must be linked into your Database's object
// file, not included in any headers that it may use (declarations are OK in
// headers but not the definitions).
//
// The "pion_create" function is used to create new instances of your Database.
// The "pion_destroy" function is used to destroy instances of your Database.
//
// extern "C" PION_PLUGIN_API Database *pion_create_DATABASE(void) {
//		return new DATABASE;
// }
//
// extern "C" PION_PLUGIN_API void pion_destroy_DATABASE(DATABASE *database_ptr) {
//		delete database_ptr;
// }
//

}	// end namespace platform
}	// end namespace pion

#endif
<commit_msg>Database -- docs<commit_after>// ------------------------------------------------------------------------
// Pion is a development platform for building Reactors that process Events
// ------------------------------------------------------------------------
// Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
//
// Pion 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.
//
// Pion 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 Pion.  If not, see <http://www.gnu.org/licenses/>.
//

#ifndef __PION_DATABASE_HEADER__
#define __PION_DATABASE_HEADER__

#include <map>
#include <string>
#include <pion/PionConfig.hpp>
#include <pion/platform/Event.hpp>
#include <pion/platform/Vocabulary.hpp>
#include <pion/platform/PlatformPlugin.hpp>
#include <pion/platform/Query.hpp>

namespace pion {		// begin namespace pion
namespace platform {	// begin namespace platform (Pion Platform Library)

///
/// Database: abstract class for storing and retrieving Events
///
class PION_PLATFORM_API Database
	: public PlatformPlugin
{
public:

	/// exception thrown if the database is busy & the query should be tried again later
	class DatabaseBusyException : public std::exception {
	public:
		virtual const char* what() const throw() {
			return "Database is busy.  Try again later";
		}
	};

	/// exception thrown if a Query cannot be found
	class QueryNotFoundException : public PionException {
	public:
		QueryNotFoundException(const std::string& query_id)
			: PionException("Unable find database query identified by: ", query_id) {}
	};

	/// exception thrown if there is an error opening a Database
	class OpenDatabaseException : public PionException {
	public:
		OpenDatabaseException(const std::string& db_name)
			: PionException("Unable to open database: ", db_name) {}
	};

	/// exception for missing configuration element
	class DatabaseConfigMissing: public PionException {
	public:
		DatabaseConfigMissing(const std::string& database_id)
			: PionException("Database template element missing: ", database_id) {}
	};

	class DatabaseClientException : public PionException {
	public:
		DatabaseClientException(const std::string& database_id)
			: PionException("Database client not defined: ", database_id) {}
	};

	/// Isolation level must be: ReadUncommitted (default), ReadCommitted, RepeatableRead, Serializable
	class InvalidIsolationLevel : public PionException {
	public:
		InvalidIsolationLevel(const std::string& database_id)
			: PionException("Invalid Isolation level defined: ", database_id) {}
	};

	/// Bad type pair in Vocabulary-to-SQL map
	class BadTypePair : public PionException {
	public:
		BadTypePair(const std::string& bad_pair)
			: PionException("Bad SQL Type pair: ", bad_pair) {}
	};

	/// Every database engine must have Vocabulary-to-SQL map
	class MissingTypeMap : public PionException {
	public:
		MissingTypeMap(const std::string& database_id)
			: PionException("SQL Type map missing: ", database_id) {}
	};

	class MissingTemplateException : public PionException {
	public:
		MissingTemplateException(const std::string& database_id)
			: PionException("Database template file not found: ", database_id) {}
	};

	class MissingRootElementException : public PionException {
	public:
		MissingRootElementException(const std::string& database_id)
			: PionException("Database template file missing root element: ", database_id) {}
	};

	class ReadConfigException: public PionException {
	public:
		ReadConfigException(const std::string& database_id)
			: PionException("Database template file not readable: ", database_id) {}
	};

	/// constructs a new Database object
	Database(void)
		: m_isolation_level(IL_LevelUnknown)
	{}

	/// virtual destructor: this class is meant to be extended
	virtual ~Database() {}

	/**
	 * retrieves a pre-compiled query for the database
	 *
	 * @param query_id string used to uniquely identify the type of query
	 * @return QueryPtr pointer to the compiled query
	 */
	inline QueryPtr getQuery(QueryID query_id) const {
		QueryMap::const_iterator i = m_query_map.find(query_id);
		if (i == m_query_map.end())
			throw QueryNotFoundException(query_id);
		return i->second;
	}

	/**
	 * clones the Database, returning a pointer to the cloned copy
	 *
	 * @return DatabasePtr pointer to the cloned copy of the Database
	 */
	virtual boost::shared_ptr<Database> clone(void) const = 0;

	/**
	 * opens the database connection
	 *
	 * @param create_backup if true, create a backup of the old database before opening
	 */
	virtual void open(bool create_backup = false) = 0;

	/// closes the database connection
	virtual void close(void) = 0;

	/// returns true if the database connection is open
	virtual bool is_open(void) const = 0;

	/**
	 * runs a simple query, ignoring any results returned
	 *
	 * @param sql_query SQL query to execute
	 */
	virtual void runQuery(const std::string& sql_query) = 0;

	/**
	 * adds a compiled SQL query to the database
	 *
	 * @param query_id string used to uniquely identify the type of query
	 * @param sql_query SQL query to compile and cache for later use
	 */
	virtual QueryPtr addQuery(QueryID query_id, const std::string& sql_query) = 0;

	/**
	 * creates a database table for output, if it does not already exist
	 *
	 * @param field_map mapping of Vocabulary Terms to Database fields
	 * @param table_name name of the table to create
	 */
	virtual void createTable(const Query::FieldMap& field_map,
							 const std::string& table_name) = 0;

	/**
	 * prepares the query that is used to insert events
	 *
	 * @param field_map mapping of Vocabulary Terms to Database fields
	 * @param table_name name of the table to insert events into
	 *
	 * @return QueryPtr smart pointer to the new query for inserting events
	 */
	virtual QueryPtr prepareInsertQuery(const Query::FieldMap& field_map,
										const std::string& table_name) = 0;

	/**
	 * returns the query that is used to begin new transactions
	 *
	 * @return QueryPtr smart pointer to the "begin transaction" query
	 */
	virtual QueryPtr getBeginTransactionQuery(void) = 0;

	/**
	 * returns the query that is used to end and commit transactions
	 *
	 * @return QueryPtr smart pointer to the "commit transaction" query
	 */
	virtual QueryPtr getCommitTransactionQuery(void) = 0;

	/**
	 * sets configuration parameters for this Database
	 *
	 * @param v the Vocabulary that this Database will use to describe Terms
	 * @param config_ptr pointer to a list of XML nodes containing Database
	 *                   configuration parameters
	 */
	virtual void setConfig(const Vocabulary& v, const xmlNodePtr config_ptr);

	/**
	 * generic simple, non-escaped, string-replace function; replace every instance of search with substitute
	 *
	 * @param src string to modify
	 * @param search string to find (and substitute)
	 * @param substitute string to put in place of search in src
	 */
	void stringReplace(std::string& src, const char* search, const std::string& substitute);

	/**
	 * replace TABLE/FIELD/COLUMNS/QUESTIONS/PARAMS with appropriate values in pseudo-SQL
	 *
	 * @param query string to modify, contains the original pseudo-SQL
	 * @param field_map fieldMap to use for substitutions of COLUMNS/PARAMS
	 * @param table_name name of table to use to substitute TABLE
	 * @return std::string returns modified query
	 */
	std::string& stringSubstitutes(std::string& query, const pion::platform::Query::FieldMap& field_map, const std::string& table_name);

	/// Database Isolation Levels
	enum IsolationLevel_t {
		IL_LevelUnknown = -1,
		IL_ReadUncommitted,
		IL_ReadCommitted,
		IL_RepeatableRead,
		IL_Serializable
	};

protected:

	/// protected copy function (use clone() instead)
	inline void copyDatabase(const Database& d) {
		PlatformPlugin::copyPlugin(d);
		// clone all the top level defined member variables
		m_database_engine = d.m_database_engine;
		m_database_client = d.m_database_client;
		m_begin_insert = d.m_begin_insert;
		m_commit_insert = d.m_commit_insert;
		m_create_log = d.m_create_log;
		m_insert_log = d.m_insert_log;
		m_create_stat = d.m_create_stat;
		m_update_stat = d.m_update_stat;
		m_select_stat = d.m_select_stat;
		m_isolation_level = d.m_isolation_level;
		// Query_map will be overridden
//		m_query_map = d.m_query_map;
		m_sql_affinity = d.m_sql_affinity;
	}

	/// data type that maps query identifiers to pointers of compiled queries
	typedef std::map<QueryID, QueryPtr>		QueryMap;

	/// unique identifier used to represent the "insert event" query
	static const std::string				INSERT_QUERY_ID;

	/// unique identifier used to represent the "begin transaction" query
	static const std::string				BEGIN_QUERY_ID;

	/// unique identifier used to represent the "commit transaction" query
	static const std::string				COMMIT_QUERY_ID;

	static const std::string				TEMPLATE_FILE;
	static const std::string				ROOT_ELEMENT_NAME;
	static const std::string				TEMPLATE_ELEMENT_NAME;
	static const std::string				MAP_ELEMENT_NAME;
	static const std::string				PAIR_ELEMENT_NAME;

	static const std::string				ENGINE_ELEMENT_NAME;

	static const std::string				CLIENT_ELEMENT_NAME;
	static const std::string				BEGIN_ELEMENT_NAME;
	static const std::string				COMMIT_ELEMENT_NAME;
	static const std::string				CREATE_LOG_ELEMENT_NAME;
	static const std::string				INSERT_LOG_ELEMENT_NAME;
	static const std::string				ISOLATION_ELEMENT_NAME;

	static const std::string				CREATE_STAT_ELEMENT_NAME;
	static const std::string				UPDATE_STAT_ELEMENT_NAME;
	static const std::string				SELECT_STAT_ELEMENT_NAME;

	/**
	 * readConfig must be called by a Database implementation as soon as it has called setConfig
	 *
	 * @param config_ptr Pointer to XML configuration (passed in as config_ptr)
	 * @param engine_str String identifying the database/engine that is being initialized
	 */
	void readConfig(const xmlNodePtr config_ptr, std::string engine_str);

	/**
	 * readConfigDetails is called by readConfig, once it locates the SQL details for an engine
	 *
	 * @param config_ptr XML file & pointer to where SQL details are found
	 */
	void readConfigDetails(const xmlNodePtr config_ptr);

	/// Database engine name, e.g. MySQL-MyISAM
	/// This is used to find a matching set of SQL from configuation file
	std::string								m_database_engine;

	/// name of database client, e.g. MySQL
	std::string								m_database_client;

	/// begin & commit insert strings
	std::string								m_begin_insert;
	std::string								m_commit_insert;
	std::string								m_create_log;
	std::string								m_insert_log;

	std::string								m_create_stat;
	std::string								m_update_stat;
	std::string								m_select_stat;

	/// Isolation level, default is SA_ReadUncommitted (for speed/ease)
	IsolationLevel_t						m_isolation_level;

	/// used to keep track of all the database's pre-compiled queries
	QueryMap								m_query_map;

	/// Affinity map; index is term_type, value should be compatible SQL type
	std::vector<std::string>				m_sql_affinity;
};

/// data type used for Database smart pointers
typedef boost::shared_ptr<Database>			DatabasePtr;

//
// The following symbols must be defined for any Databases that you would
// like to be able to load dynamically using the DatabaseManager::load()
// function.  These are not required for any Databases that you only want
// to link directly into your programs.
//
// Make sure that you replace "DATABASE" with the name of your derived class.
// This name must also match the name of the object file (excluding the
// extension).  These symbols must be linked into your Database's object
// file, not included in any headers that it may use (declarations are OK in
// headers but not the definitions).
//
// The "pion_create" function is used to create new instances of your Database.
// The "pion_destroy" function is used to destroy instances of your Database.
//
// extern "C" PION_PLUGIN_API Database *pion_create_DATABASE(void) {
//		return new DATABASE;
// }
//
// extern "C" PION_PLUGIN_API void pion_destroy_DATABASE(DATABASE *database_ptr) {
//		delete database_ptr;
// }
//

}	// end namespace platform
}	// end namespace pion

#endif
<|endoftext|>


<commit_before>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include "TestBlock.hpp"

#include <nix/util/util.hpp>

#include <nix/valid/validate.hpp>
#include <nix/Exception.hpp>

#include <ctime>

using namespace std;
using namespace nix;
using namespace valid;

void TestBlock::setUp() {
    startup_time = time(NULL);
    file = File::open("test_block.h5", FileMode::Overwrite);

    section = file.createSection("foo_section", "metadata");

    block = file.createBlock("block_one", "dataset");
    block_other = file.createBlock("block_two", "dataset");
    block_null  = nullptr;
}

void TestBlock::tearDown() {
    file.close();
}

void TestBlock::testValidate() {
    valid::Result result = validate(block);
    CPPUNIT_ASSERT(result.getErrors().size() == 0);
    CPPUNIT_ASSERT(result.getWarnings().size() == 0);
}

void TestBlock::testId() {
    CPPUNIT_ASSERT(block.id().size() == 36);
}

void TestBlock::testName() {
    CPPUNIT_ASSERT(block.name() == "block_one");
}

void TestBlock::testType() {
    CPPUNIT_ASSERT(block.type() == "dataset");
    string typ = util::createId();
    block.type(typ);
    CPPUNIT_ASSERT(block.type() == typ);
}

void TestBlock::testDefinition() {
    string def = util::createId();
    block.definition(def);
    CPPUNIT_ASSERT(*block.definition() == def);
}

void TestBlock::testMetadataAccess() {
    CPPUNIT_ASSERT(!block.metadata());

    block.metadata(section);
    CPPUNIT_ASSERT(block.metadata());

    // test none-unsetter
    block.metadata(none);
    CPPUNIT_ASSERT(!block.metadata());
    // test deleter removing link too
    block.metadata(section);
    file.deleteSection(section.id());
    CPPUNIT_ASSERT(!block.metadata());
    // re-create section
    section = file.createSection("foo_section", "metadata");
}

void TestBlock::testSourceAccess() {
    vector<string> names = { "source_a", "source_b", "source_c", "source_d", "source_e" };
    Source s;
    CPPUNIT_ASSERT_THROW(block.hasSource(s), std::runtime_error);
    CPPUNIT_ASSERT(block.sourceCount() == 0);
    CPPUNIT_ASSERT(block.sources().size() == 0);
    CPPUNIT_ASSERT(block.getSource("invalid_id") == false);
    CPPUNIT_ASSERT(!block.hasSource("invalid_id"));

    vector<string> ids;
    for (const auto &name : names) {
        Source src = block.createSource(name, "channel");
        CPPUNIT_ASSERT(src.name() == name);
        CPPUNIT_ASSERT(block.hasSource(name));
        CPPUNIT_ASSERT(block.hasSource(src));

        ids.push_back(src.id());
    }
    CPPUNIT_ASSERT_THROW(block.createSource(names[0], "channel"),
                         DuplicateName);

    CPPUNIT_ASSERT(block.sourceCount() == names.size());
    CPPUNIT_ASSERT(block.sources().size() == names.size());

    for (const auto &id : ids) {
        Source src = block.getSource(id);
        CPPUNIT_ASSERT(block.hasSource(id) == true);
        CPPUNIT_ASSERT(src.id() == id);
        block.deleteSource(id);
    }

    s = block.createSource("test", "test");
    CPPUNIT_ASSERT(block.sourceCount() == 1);
    CPPUNIT_ASSERT_NO_THROW(block.deleteSource(s));

    CPPUNIT_ASSERT(block.sourceCount() == 0);
    CPPUNIT_ASSERT(block.sources().size() == 0);
    CPPUNIT_ASSERT(block.getSource("invalid_id") == false);
}

void TestBlock::testDataArrayAccess() {
    vector<string> names = { "data_array_a", "data_array_b", "data_array_c",
                             "data_array_d", "data_array_e" };
    DataArray data_array, a;

    CPPUNIT_ASSERT(block.dataArrayCount() == 0);
    CPPUNIT_ASSERT(block.dataArrays().size() == 0);
    CPPUNIT_ASSERT(block.getDataArray("invalid_id") == false);

    vector<string> ids;
    for (const auto &name : names) {
        data_array = block.createDataArray(name, "channel",
                                           DataType::Double, nix::NDSize({ 0 }));
        CPPUNIT_ASSERT(data_array.name() == name);
        CPPUNIT_ASSERT(data_array.type() == "channel");

        ids.push_back(data_array.id());
    }
    CPPUNIT_ASSERT_THROW(block.createDataArray(names[0], "channel", DataType::Double, nix::NDSize({ 0 })),
                         DuplicateName);
    CPPUNIT_ASSERT(block.hasDataArray(data_array));
    CPPUNIT_ASSERT_THROW(block.hasDataArray(a), std::runtime_error);
    CPPUNIT_ASSERT(block.dataArrayCount() == names.size());
    CPPUNIT_ASSERT(block.dataArrays().size() == names.size());

    for (const auto &name : names) {
        DataArray da_name = block.getDataArray(name);
        CPPUNIT_ASSERT(da_name);

        DataArray da_id = block.getDataArray(da_name.id());
        CPPUNIT_ASSERT(da_id);
        CPPUNIT_ASSERT_EQUAL(da_name.name(), da_id.name());
    }

    vector<DataArray> filteredArrays = block.dataArrays(util::TypeFilter<DataArray>("channel"));
    CPPUNIT_ASSERT_EQUAL(names.size(), filteredArrays.size());

    filteredArrays = block.dataArrays(util::NameFilter<DataArray>("data_array_c"));
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), filteredArrays.size());
    if (filteredArrays.size() > 0) {
        boost::optional<std::string> name = filteredArrays[0].name();
        CPPUNIT_ASSERT(name && *name == "data_array_c");
    }

    for (auto it = ids.begin(); it != ids.end(); it++) {
        DataArray data_array = block.getDataArray(*it);
        CPPUNIT_ASSERT(block.hasDataArray(*it) == true);
        CPPUNIT_ASSERT(data_array.id() == *it);

        block.deleteDataArray(*it);
    }
    CPPUNIT_ASSERT_THROW(block.deleteDataArray(a), std::runtime_error);
    CPPUNIT_ASSERT(block.dataArrayCount() == 0);
    CPPUNIT_ASSERT(block.dataArrays().size() == 0);
    CPPUNIT_ASSERT(block.getDataArray("invalid_id") == false);

}

void TestBlock::testTagAccess() {
    vector<string> names = { "tag_a", "tag_b", "tag_c", "tag_d", "tag_e" };
    vector<string> array_names = { "data_array_a", "data_array_b", "data_array_c",
                                   "data_array_d", "data_array_e" };
    vector<DataArray> refs;
    for (const auto &name : array_names) {
        refs.push_back(block.createDataArray(name,
                                             "reference",
                                             DataType::Double,
                                             nix::NDSize({ 0 })));
    }

    CPPUNIT_ASSERT(block.tagCount() == 0);
    CPPUNIT_ASSERT(block.tags().size() == 0);
    CPPUNIT_ASSERT(block.getTag("invalid_id") == false);

    vector<string> ids;
    for (auto it = names.begin(); it != names.end(); ++it) {
        Tag tag = block.createTag(*it, "segment", {0.0, 2.0, 3.4});
        tag.references(refs);
        CPPUNIT_ASSERT(tag.name() == *it);

        ids.push_back(tag.id());
    }
    CPPUNIT_ASSERT_THROW(block.createTag(names[0], "segment", {0.0, 2.0, 3.4}),
                         DuplicateName);

    CPPUNIT_ASSERT(block.tagCount() == names.size());
    CPPUNIT_ASSERT(block.tags().size() == names.size());

    for (auto it = ids.begin(); it != ids.end(); ++it) {
        Tag tag = block.getTag(*it);
        CPPUNIT_ASSERT(block.hasTag(*it) == true);
        CPPUNIT_ASSERT(tag.id() == *it);

        block.deleteTag(*it);
    }

    CPPUNIT_ASSERT(block.tagCount() == 0);
    CPPUNIT_ASSERT(block.tags().size() == 0);
    CPPUNIT_ASSERT(block.getTag("invalid_id") == false);
}

void TestBlock::testMultiTagAccess() {
    vector<string> names = { "tag_a", "tag_b", "tag_c", "tag_d", "tag_e" };
    // create a valid positions data array below
    typedef boost::multi_array<double, 3>::index index;
    DataArray positions = block.createDataArray("array_one",
                                                "testdata",
                                                DataType::Double,
                                                nix::NDSize({ 3, 4, 2 }));
    boost::multi_array<double, 3> A(boost::extents[3][4][2]);
    int values = 0;
    for(index i = 0; i != 3; ++i)
        for(index j = 0; j != 4; ++j)
            for(index k = 0; k != 2; ++k)
                A[i][j][k] = values++;

    positions.setData(A);

    CPPUNIT_ASSERT(block.multiTagCount() == 0);
    CPPUNIT_ASSERT(block.multiTags().size() == 0);
    CPPUNIT_ASSERT(block.getMultiTag("invalid_id") == false);

    vector<string> ids;
    for (auto it = names.begin(); it != names.end(); it++) {
        MultiTag tag = block.createMultiTag(*it, "segment", positions);
        CPPUNIT_ASSERT(tag.name() == *it);

        ids.push_back(tag.id());
    }
    CPPUNIT_ASSERT_THROW(block.createMultiTag(names[0], "segment", positions),
                         DuplicateName);

    CPPUNIT_ASSERT(block.multiTagCount() == names.size());
    CPPUNIT_ASSERT(block.multiTags().size() == names.size());

    for (auto it = ids.begin(); it != ids.end(); it++) {
        MultiTag tag = block.getMultiTag(*it);
        CPPUNIT_ASSERT(block.hasMultiTag(*it) == true);
        CPPUNIT_ASSERT(tag.id() == *it);

        block.deleteMultiTag(*it);
    }

    CPPUNIT_ASSERT(block.multiTagCount() == 0);
    CPPUNIT_ASSERT(block.multiTags().size() == 0);
    CPPUNIT_ASSERT(block.getMultiTag("invalid_id") == false);
}

void TestBlock::testOperators() {
    CPPUNIT_ASSERT(block_null == false);
    CPPUNIT_ASSERT(block_null == none);

    CPPUNIT_ASSERT(block != false);
    CPPUNIT_ASSERT(block != none);

    CPPUNIT_ASSERT(block == block);
    CPPUNIT_ASSERT(block != block_other);

    block_other = block;
    CPPUNIT_ASSERT(block == block_other);

    block_other = none;
    CPPUNIT_ASSERT(block_null == false);
    CPPUNIT_ASSERT(block_null == none);
}

void TestBlock::testCreatedAt() {
    CPPUNIT_ASSERT(block.createdAt() >= startup_time);
    time_t past_time = time(NULL) - 10000000;
    block.forceCreatedAt(past_time);
    CPPUNIT_ASSERT(block.createdAt() == past_time);
}

void TestBlock::testUpdatedAt() {
    CPPUNIT_ASSERT(block.updatedAt() >= startup_time);
}
<commit_msg>[testBlock] complete tests for tag access<commit_after>// Copyright (c) 2013, German Neuroinformatics Node (G-Node)
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted under the terms of the BSD License. See
// LICENSE file in the root of the Project.

#include "TestBlock.hpp"

#include <nix/util/util.hpp>

#include <nix/valid/validate.hpp>
#include <nix/Exception.hpp>

#include <ctime>

using namespace std;
using namespace nix;
using namespace valid;

void TestBlock::setUp() {
    startup_time = time(NULL);
    file = File::open("test_block.h5", FileMode::Overwrite);

    section = file.createSection("foo_section", "metadata");

    block = file.createBlock("block_one", "dataset");
    block_other = file.createBlock("block_two", "dataset");
    block_null  = nullptr;
}

void TestBlock::tearDown() {
    file.close();
}

void TestBlock::testValidate() {
    valid::Result result = validate(block);
    CPPUNIT_ASSERT(result.getErrors().size() == 0);
    CPPUNIT_ASSERT(result.getWarnings().size() == 0);
}

void TestBlock::testId() {
    CPPUNIT_ASSERT(block.id().size() == 36);
}

void TestBlock::testName() {
    CPPUNIT_ASSERT(block.name() == "block_one");
}

void TestBlock::testType() {
    CPPUNIT_ASSERT(block.type() == "dataset");
    string typ = util::createId();
    block.type(typ);
    CPPUNIT_ASSERT(block.type() == typ);
}

void TestBlock::testDefinition() {
    string def = util::createId();
    block.definition(def);
    CPPUNIT_ASSERT(*block.definition() == def);
}

void TestBlock::testMetadataAccess() {
    CPPUNIT_ASSERT(!block.metadata());

    block.metadata(section);
    CPPUNIT_ASSERT(block.metadata());

    // test none-unsetter
    block.metadata(none);
    CPPUNIT_ASSERT(!block.metadata());
    // test deleter removing link too
    block.metadata(section);
    file.deleteSection(section.id());
    CPPUNIT_ASSERT(!block.metadata());
    // re-create section
    section = file.createSection("foo_section", "metadata");
}

void TestBlock::testSourceAccess() {
    vector<string> names = { "source_a", "source_b", "source_c", "source_d", "source_e" };
    Source s;
    CPPUNIT_ASSERT_THROW(block.hasSource(s), std::runtime_error);
    CPPUNIT_ASSERT(block.sourceCount() == 0);
    CPPUNIT_ASSERT(block.sources().size() == 0);
    CPPUNIT_ASSERT(block.getSource("invalid_id") == false);
    CPPUNIT_ASSERT(!block.hasSource("invalid_id"));

    vector<string> ids;
    for (const auto &name : names) {
        Source src = block.createSource(name, "channel");
        CPPUNIT_ASSERT(src.name() == name);
        CPPUNIT_ASSERT(block.hasSource(name));
        CPPUNIT_ASSERT(block.hasSource(src));

        ids.push_back(src.id());
    }
    CPPUNIT_ASSERT_THROW(block.createSource(names[0], "channel"),
                         DuplicateName);

    CPPUNIT_ASSERT(block.sourceCount() == names.size());
    CPPUNIT_ASSERT(block.sources().size() == names.size());

    for (const auto &id : ids) {
        Source src = block.getSource(id);
        CPPUNIT_ASSERT(block.hasSource(id) == true);
        CPPUNIT_ASSERT(src.id() == id);
        block.deleteSource(id);
    }

    s = block.createSource("test", "test");
    CPPUNIT_ASSERT(block.sourceCount() == 1);
    CPPUNIT_ASSERT_NO_THROW(block.deleteSource(s));

    CPPUNIT_ASSERT(block.sourceCount() == 0);
    CPPUNIT_ASSERT(block.sources().size() == 0);
    CPPUNIT_ASSERT(block.getSource("invalid_id") == false);
}

void TestBlock::testDataArrayAccess() {
    vector<string> names = { "data_array_a", "data_array_b", "data_array_c",
                             "data_array_d", "data_array_e" };
    DataArray data_array, a;

    CPPUNIT_ASSERT(block.dataArrayCount() == 0);
    CPPUNIT_ASSERT(block.dataArrays().size() == 0);
    CPPUNIT_ASSERT(block.getDataArray("invalid_id") == false);

    vector<string> ids;
    for (const auto &name : names) {
        data_array = block.createDataArray(name, "channel",
                                           DataType::Double, nix::NDSize({ 0 }));
        CPPUNIT_ASSERT(data_array.name() == name);
        CPPUNIT_ASSERT(data_array.type() == "channel");

        ids.push_back(data_array.id());
    }
    CPPUNIT_ASSERT_THROW(block.createDataArray(names[0], "channel", DataType::Double, nix::NDSize({ 0 })),
                         DuplicateName);
    CPPUNIT_ASSERT(block.hasDataArray(data_array));
    CPPUNIT_ASSERT_THROW(block.hasDataArray(a), std::runtime_error);
    CPPUNIT_ASSERT(block.dataArrayCount() == names.size());
    CPPUNIT_ASSERT(block.dataArrays().size() == names.size());

    for (const auto &name : names) {
        DataArray da_name = block.getDataArray(name);
        CPPUNIT_ASSERT(da_name);

        DataArray da_id = block.getDataArray(da_name.id());
        CPPUNIT_ASSERT(da_id);
        CPPUNIT_ASSERT_EQUAL(da_name.name(), da_id.name());
    }

    vector<DataArray> filteredArrays = block.dataArrays(util::TypeFilter<DataArray>("channel"));
    CPPUNIT_ASSERT_EQUAL(names.size(), filteredArrays.size());

    filteredArrays = block.dataArrays(util::NameFilter<DataArray>("data_array_c"));
    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), filteredArrays.size());
    if (filteredArrays.size() > 0) {
        boost::optional<std::string> name = filteredArrays[0].name();
        CPPUNIT_ASSERT(name && *name == "data_array_c");
    }

    for (auto it = ids.begin(); it != ids.end(); it++) {
        DataArray data_array = block.getDataArray(*it);
        CPPUNIT_ASSERT(block.hasDataArray(*it) == true);
        CPPUNIT_ASSERT(data_array.id() == *it);

        block.deleteDataArray(*it);
    }
    CPPUNIT_ASSERT_THROW(block.deleteDataArray(a), std::runtime_error);
    CPPUNIT_ASSERT(block.dataArrayCount() == 0);
    CPPUNIT_ASSERT(block.dataArrays().size() == 0);
    CPPUNIT_ASSERT(block.getDataArray("invalid_id") == false);

}

void TestBlock::testTagAccess() {
    vector<string> names = { "tag_a", "tag_b", "tag_c", "tag_d", "tag_e" };
    vector<string> array_names = { "data_array_a", "data_array_b", "data_array_c",
                                   "data_array_d", "data_array_e" };
    vector<DataArray> refs;
    Tag tag, t;
    for (const auto &name : array_names) {
        refs.push_back(block.createDataArray(name,
                                             "reference",
                                             DataType::Double,
                                             nix::NDSize({ 0 })));
    }

    CPPUNIT_ASSERT(block.tagCount() == 0);
    CPPUNIT_ASSERT(block.tags().size() == 0);
    CPPUNIT_ASSERT(block.getTag("invalid_id") == false);
    CPPUNIT_ASSERT_THROW(block.hasTag(t), std::runtime_error);

    vector<string> ids;
    for (auto it = names.begin(); it != names.end(); ++it) {
        tag = block.createTag(*it, "segment", {0.0, 2.0, 3.4});
        tag.references(refs);
        CPPUNIT_ASSERT(tag.name() == *it);
        CPPUNIT_ASSERT(block.hasTag(tag));
        ids.push_back(tag.id());
    }
    CPPUNIT_ASSERT_THROW(block.createTag(names[0], "segment", {0.0, 2.0, 3.4}),
                         DuplicateName);

    CPPUNIT_ASSERT(block.tagCount() == names.size());
    CPPUNIT_ASSERT(block.tags().size() == names.size());

    for (auto it = ids.begin(); it != ids.end(); ++it) {
        tag = block.getTag(*it);
        CPPUNIT_ASSERT(block.hasTag(*it) == true);
        CPPUNIT_ASSERT(tag.id() == *it);

        block.deleteTag(*it);
    }

    tag = block.createTag("test", "test", {0.0});
    CPPUNIT_ASSERT(block.hasTag(tag));
    CPPUNIT_ASSERT_NO_THROW(block.deleteTag(tag));
    CPPUNIT_ASSERT_THROW(block.deleteTag(t), std::runtime_error);

    CPPUNIT_ASSERT(block.tagCount() == 0);
    CPPUNIT_ASSERT(block.tags().size() == 0);
    CPPUNIT_ASSERT(block.getTag("invalid_id") == false);
}

void TestBlock::testMultiTagAccess() {
    vector<string> names = { "tag_a", "tag_b", "tag_c", "tag_d", "tag_e" };
    // create a valid positions data array below
    typedef boost::multi_array<double, 3>::index index;
    DataArray positions = block.createDataArray("array_one",
                                                "testdata",
                                                DataType::Double,
                                                nix::NDSize({ 3, 4, 2 }));
    boost::multi_array<double, 3> A(boost::extents[3][4][2]);
    int values = 0;
    for(index i = 0; i != 3; ++i)
        for(index j = 0; j != 4; ++j)
            for(index k = 0; k != 2; ++k)
                A[i][j][k] = values++;

    positions.setData(A);

    CPPUNIT_ASSERT(block.multiTagCount() == 0);
    CPPUNIT_ASSERT(block.multiTags().size() == 0);
    CPPUNIT_ASSERT(block.getMultiTag("invalid_id") == false);

    vector<string> ids;
    for (auto it = names.begin(); it != names.end(); it++) {
        MultiTag tag = block.createMultiTag(*it, "segment", positions);
        CPPUNIT_ASSERT(tag.name() == *it);

        ids.push_back(tag.id());
    }
    CPPUNIT_ASSERT_THROW(block.createMultiTag(names[0], "segment", positions),
                         DuplicateName);

    CPPUNIT_ASSERT(block.multiTagCount() == names.size());
    CPPUNIT_ASSERT(block.multiTags().size() == names.size());

    for (auto it = ids.begin(); it != ids.end(); it++) {
        MultiTag tag = block.getMultiTag(*it);
        CPPUNIT_ASSERT(block.hasMultiTag(*it) == true);
        CPPUNIT_ASSERT(tag.id() == *it);

        block.deleteMultiTag(*it);
    }

    CPPUNIT_ASSERT(block.multiTagCount() == 0);
    CPPUNIT_ASSERT(block.multiTags().size() == 0);
    CPPUNIT_ASSERT(block.getMultiTag("invalid_id") == false);
}

void TestBlock::testOperators() {
    CPPUNIT_ASSERT(block_null == false);
    CPPUNIT_ASSERT(block_null == none);

    CPPUNIT_ASSERT(block != false);
    CPPUNIT_ASSERT(block != none);

    CPPUNIT_ASSERT(block == block);
    CPPUNIT_ASSERT(block != block_other);

    block_other = block;
    CPPUNIT_ASSERT(block == block_other);

    block_other = none;
    CPPUNIT_ASSERT(block_null == false);
    CPPUNIT_ASSERT(block_null == none);
}

void TestBlock::testCreatedAt() {
    CPPUNIT_ASSERT(block.createdAt() >= startup_time);
    time_t past_time = time(NULL) - 10000000;
    block.forceCreatedAt(past_time);
    CPPUNIT_ASSERT(block.createdAt() == past_time);
}

void TestBlock::testUpdatedAt() {
    CPPUNIT_ASSERT(block.updatedAt() >= startup_time);
}
<|endoftext|>


<commit_before>/// @file
/// @version 3.33
///
/// @section LICENSE
///
/// This program is free software; you can redistribute it and/or modify it under
/// the terms of the BSD license: http://opensource.org/licenses/BSD-3-Clause

#ifdef _DIRECTSOUND
#include <dsound.h>

#include <hltypes/hlog.h>
#include <hltypes/hltypesUtil.h>
#include <hltypes/hplatform.h>
#include <hltypes/hstring.h>

#include "Buffer.h"
#include "DirectSound_Player.h"
#include "DirectSound_AudioManager.h"
#include "Sound.h"
#include "xal.h"

namespace xal
{
	DirectSound_Player::DirectSound_Player(Sound* sound) : Player(sound),
		dsBuffer(NULL), bufferCount(0), bufferQueued(0)
	{
	}

	DirectSound_Player::~DirectSound_Player()
	{
		// AudioManager calls _stop before destruction
		if (this->dsBuffer != NULL)
		{
			this->dsBuffer->Release();
			this->dsBuffer = NULL;
		}
	}

	bool DirectSound_Player::_systemIsPlaying()
	{
		if (this->dsBuffer == NULL)
		{
			return false;
		}
		unsigned long status;
		this->dsBuffer->GetStatus(&status);
		return ((status & DSBSTATUS_PLAYING) != 0);
	}

	unsigned int DirectSound_Player::_systemGetBufferPosition()
	{
		unsigned long position = 0;
		if (this->dsBuffer != NULL)
		{
			this->dsBuffer->GetCurrentPosition(&position, NULL);
		}
		return (unsigned int)position;
	}

	float DirectSound_Player::_systemGetOffset()
	{
		return (float)this->_systemGetBufferPosition();
	}

	void DirectSound_Player::_systemSetOffset(float value)
	{
		if (this->dsBuffer != NULL)
		{
			this->dsBuffer->SetCurrentPosition((DWORD)value);
		}
	}

	bool DirectSound_Player::_systemPreparePlay()
	{
		if (this->dsBuffer != NULL)
		{
			return true;
		}
		WAVEFORMATEX wavefmt;
		wavefmt.cbSize = 0;
		wavefmt.nChannels = this->buffer->getChannels();
		wavefmt.nSamplesPerSec = this->buffer->getSamplingRate();
		wavefmt.wBitsPerSample = this->buffer->getBitsPerSample();
		wavefmt.wFormatTag = WAVE_FORMAT_PCM;
		wavefmt.nBlockAlign = wavefmt.nChannels * wavefmt.wBitsPerSample / 8; // standard calculation of WAV PCM data
		wavefmt.nAvgBytesPerSec = wavefmt.nSamplesPerSec * wavefmt.nBlockAlign; // standard calculation of WAV PCM data
		DSBUFFERDESC bufferDesc;
		memset(&bufferDesc, 0, sizeof(DSBUFFERDESC));
		bufferDesc.dwSize = sizeof(DSBUFFERDESC);
		bufferDesc.dwFlags = (DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS);
		bufferDesc.dwBufferBytes = (!this->sound->isStreamed() ? this->buffer->getSize() : STREAM_BUFFER);
		bufferDesc.lpwfxFormat = &wavefmt;
		HRESULT result = ((DirectSound_AudioManager*)xal::mgr)->dsDevice->CreateSoundBuffer(&bufferDesc, &this->dsBuffer, NULL);
		if (FAILED(result))
		{
			this->dsBuffer = NULL;
			return false;
		}
		return true;
	}

	void DirectSound_Player::_systemPrepareBuffer()
	{
		if (!this->sound->isStreamed())
		{
			hstream& stream = this->buffer->getStream();
			this->_copyBuffer(stream, (int)stream.size());
			return;
		}
		int count = STREAM_BUFFER_COUNT;
		if (!this->paused)
		{
			this->bufferIndex = 0;
		}
		else
		{
			count -= this->bufferQueued;
		}
		if (count > 0)
		{
			count = this->_fillBuffers(this->bufferIndex, count);
			this->bufferCount += count;
			if (count > 0)
			{
				this->_copyBuffer(this->buffer->getStream(), STREAM_BUFFER_SIZE, count);
			}
			if (this->bufferQueued < STREAM_BUFFER_COUNT)
			{
				count = STREAM_BUFFER_COUNT - this->bufferQueued;
				this->_copySilence(STREAM_BUFFER_SIZE, count);
			}
		}
	}

	void DirectSound_Player::_copyBuffer(hstream& stream, int size, int count)
	{
		void* write1 = NULL;
		void* write2 = NULL;
		unsigned long length1;
		unsigned long length2;
		int lockOffset = 0;
		if (this->sound->isStreamed())
		{
			lockOffset = this->bufferIndex * STREAM_BUFFER_SIZE;
		}
		HRESULT result = this->dsBuffer->Lock(lockOffset, size * count, &write1, &length1, &write2, &length2, 0);
		if (FAILED(result))
		{
			hlog::error(xal::logTag, "Cannot lock buffer for: " + this->sound->getFilename());
			return;
		}
		if (write1 != NULL)
		{
			memcpy(write1, &stream[0], length1);
		}
		if (write2 != NULL)
		{
			memcpy(write2, &stream[length1], length2);
		}
		this->dsBuffer->Unlock(write1, length1, write2, length2);
		if (this->sound->isStreamed())
		{
			this->bufferIndex = (this->bufferIndex + count) % STREAM_BUFFER_COUNT;
			this->bufferQueued += count;
		}
	}

	void DirectSound_Player::_copySilence(int size, int count)
	{
		void* write1 = NULL;
		void* write2 = NULL;
		unsigned long length1;
		unsigned long length2;
		int lockOffset = 0;
		if (this->sound->isStreamed())
		{
			lockOffset = this->bufferIndex * STREAM_BUFFER_SIZE;
		}
		HRESULT result = this->dsBuffer->Lock(lockOffset, size * count, &write1, &length1, &write2, &length2, 0);
		if (FAILED(result))
		{
			hlog::error(xal::logTag, "Cannot lock buffer for: " + this->sound->getFilename());
			return;
		}
		if (write1 != NULL)
		{
			memset(write1, 0, length1);
		}
		if (write2 != NULL)
		{
			memset(write2, 0, length2);
		}
		this->dsBuffer->Unlock(write1, length1, write2, length2);
		if (this->sound->isStreamed())
		{
			this->bufferIndex = (this->bufferIndex + count) % STREAM_BUFFER_COUNT;
			this->bufferQueued += count;
		}
	}

	void DirectSound_Player::_systemUpdateGain()
	{
		if (this->dsBuffer != NULL)
		{
			float gain = this->_calcGain();
			LONG value = DSBVOLUME_MIN;
			if (gain > 0.0f)
			{
				value = (LONG)(log10(gain) / 4 * (DSBVOLUME_MAX - DSBVOLUME_MIN));
			}
			this->dsBuffer->SetVolume(value);
		}
	}

	void DirectSound_Player::_systemUpdatePitch()
	{
		if (this->dsBuffer != NULL)
		{
			DWORD freq = (DWORD)(this->pitch * this->buffer->getSamplingRate());
			DWORD limit = ((DirectSound_AudioManager*)xal::mgr)->dsCaps->dwMaxSecondarySampleRate;
			this->dsBuffer->SetFrequency(hmin(freq, limit));
		}
	}

	void DirectSound_Player::_systemPlay()
	{
		if (this->dsBuffer != NULL)
		{
			this->dsBuffer->Play(0, 0, ((this->looping || this->sound->isStreamed()) ? DSBPLAY_LOOPING : 0));
		}
	}

	int DirectSound_Player::_systemStop()
	{
		int result = 0;
		if (this->dsBuffer != NULL)
		{
			this->dsBuffer->Stop();
			if (this->sound->isStreamed())
			{
				if (this->paused)
				{
					int processed = this->_getProcessedBuffersCount();
					this->bufferIndex = (this->bufferIndex + processed) % STREAM_BUFFER_COUNT;
					this->bufferCount -= processed;
					this->bufferQueued -= processed;
					result = processed * STREAM_BUFFER_SIZE;
				}
				else
				{
					this->bufferIndex = 0;
					this->bufferCount = 0;
					this->bufferQueued = 0;
					this->buffer->rewind();
				}
			}
			if (!this->paused)
			{
				this->_systemSetOffset(0); // reset buffer position 0 on stop
			}
		}
		return result;
	}

	int DirectSound_Player::_systemUpdateStream()
	{
		if (this->bufferCount == 0)
		{
			this->_stop();
			return 0;
		}
		int processed = this->_getProcessedBuffersCount();
		if (processed == 0)
		{
			return 0;
		}
		this->bufferCount = hmax(this->bufferCount - processed, 0);
		this->bufferQueued = hmax(this->bufferQueued - processed, 0);
		int count = this->_fillBuffers(this->bufferIndex, processed);
		if (count > 0)
		{
			this->_copyBuffer(this->buffer->getStream(), STREAM_BUFFER_SIZE, count);
			this->bufferCount += count;
		}
		if (!this->looping && this->bufferQueued < STREAM_BUFFER_COUNT)
		{
			count = STREAM_BUFFER_COUNT - this->bufferQueued;
			this->_copySilence(STREAM_BUFFER_SIZE, count);
			this->bufferCount -= count; // full silence buffers are a safe-guard, not really filled buffers
		}
		if (this->bufferCount == 0)
		{
			this->_stop();
			processed = 0;
		}
		return (processed * STREAM_BUFFER_SIZE);
	}

	int DirectSound_Player::_getProcessedBuffersCount()
	{
		return ((this->_systemGetBufferPosition() / STREAM_BUFFER_SIZE +
			STREAM_BUFFER_COUNT - this->bufferIndex) % STREAM_BUFFER_COUNT);
	}

	int DirectSound_Player::_fillBuffers(int index, int count)
	{
		int size = this->buffer->load(this->looping, count * STREAM_BUFFER_SIZE);
		return (size + STREAM_BUFFER_SIZE - 1) / STREAM_BUFFER_SIZE;
	}

}
#endif<commit_msg>- fixed DirectSound bug with streamed non-looping sounds that would stop prematurely before the end (turns out the "silence" buffer abort fix from the previous commit caused this)<commit_after>/// @file
/// @version 3.33
///
/// @section LICENSE
///
/// This program is free software; you can redistribute it and/or modify it under
/// the terms of the BSD license: http://opensource.org/licenses/BSD-3-Clause

#ifdef _DIRECTSOUND
#include <dsound.h>

#include <hltypes/hlog.h>
#include <hltypes/hltypesUtil.h>
#include <hltypes/hplatform.h>
#include <hltypes/hstring.h>

#include "Buffer.h"
#include "DirectSound_Player.h"
#include "DirectSound_AudioManager.h"
#include "Sound.h"
#include "xal.h"

namespace xal
{
	DirectSound_Player::DirectSound_Player(Sound* sound) : Player(sound),
		dsBuffer(NULL), bufferCount(0), bufferQueued(0)
	{
	}

	DirectSound_Player::~DirectSound_Player()
	{
		// AudioManager calls _stop before destruction
		if (this->dsBuffer != NULL)
		{
			this->dsBuffer->Release();
			this->dsBuffer = NULL;
		}
	}

	bool DirectSound_Player::_systemIsPlaying()
	{
		if (this->dsBuffer == NULL)
		{
			return false;
		}
		unsigned long status;
		this->dsBuffer->GetStatus(&status);
		return ((status & DSBSTATUS_PLAYING) != 0);
	}

	unsigned int DirectSound_Player::_systemGetBufferPosition()
	{
		unsigned long position = 0;
		if (this->dsBuffer != NULL)
		{
			this->dsBuffer->GetCurrentPosition(&position, NULL);
		}
		return (unsigned int)position;
	}

	float DirectSound_Player::_systemGetOffset()
	{
		return (float)this->_systemGetBufferPosition();
	}

	void DirectSound_Player::_systemSetOffset(float value)
	{
		if (this->dsBuffer != NULL)
		{
			this->dsBuffer->SetCurrentPosition((DWORD)value);
		}
	}

	bool DirectSound_Player::_systemPreparePlay()
	{
		if (this->dsBuffer != NULL)
		{
			return true;
		}
		WAVEFORMATEX wavefmt;
		wavefmt.cbSize = 0;
		wavefmt.nChannels = this->buffer->getChannels();
		wavefmt.nSamplesPerSec = this->buffer->getSamplingRate();
		wavefmt.wBitsPerSample = this->buffer->getBitsPerSample();
		wavefmt.wFormatTag = WAVE_FORMAT_PCM;
		wavefmt.nBlockAlign = wavefmt.nChannels * wavefmt.wBitsPerSample / 8; // standard calculation of WAV PCM data
		wavefmt.nAvgBytesPerSec = wavefmt.nSamplesPerSec * wavefmt.nBlockAlign; // standard calculation of WAV PCM data
		DSBUFFERDESC bufferDesc;
		memset(&bufferDesc, 0, sizeof(DSBUFFERDESC));
		bufferDesc.dwSize = sizeof(DSBUFFERDESC);
		bufferDesc.dwFlags = (DSBCAPS_CTRLVOLUME | DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPOSITIONNOTIFY | DSBCAPS_GLOBALFOCUS);
		bufferDesc.dwBufferBytes = (!this->sound->isStreamed() ? this->buffer->getSize() : STREAM_BUFFER);
		bufferDesc.lpwfxFormat = &wavefmt;
		HRESULT result = ((DirectSound_AudioManager*)xal::mgr)->dsDevice->CreateSoundBuffer(&bufferDesc, &this->dsBuffer, NULL);
		if (FAILED(result))
		{
			this->dsBuffer = NULL;
			return false;
		}
		return true;
	}

	void DirectSound_Player::_systemPrepareBuffer()
	{
		if (!this->sound->isStreamed())
		{
			hstream& stream = this->buffer->getStream();
			this->_copyBuffer(stream, (int)stream.size());
			return;
		}
		int count = STREAM_BUFFER_COUNT;
		if (!this->paused)
		{
			this->bufferIndex = 0;
		}
		else
		{
			count -= this->bufferQueued;
		}
		if (count > 0)
		{
			count = this->_fillBuffers(this->bufferIndex, count);
			this->bufferCount += count;
			if (count > 0)
			{
				this->_copyBuffer(this->buffer->getStream(), STREAM_BUFFER_SIZE, count);
			}
			if (this->bufferQueued < STREAM_BUFFER_COUNT)
			{
				count = STREAM_BUFFER_COUNT - this->bufferQueued;
				this->_copySilence(STREAM_BUFFER_SIZE, count);
			}
		}
	}

	void DirectSound_Player::_copyBuffer(hstream& stream, int size, int count)
	{
		void* write1 = NULL;
		void* write2 = NULL;
		unsigned long length1;
		unsigned long length2;
		int lockOffset = 0;
		if (this->sound->isStreamed())
		{
			lockOffset = this->bufferIndex * STREAM_BUFFER_SIZE;
		}
		HRESULT result = this->dsBuffer->Lock(lockOffset, size * count, &write1, &length1, &write2, &length2, 0);
		if (FAILED(result))
		{
			hlog::error(xal::logTag, "Cannot lock buffer for: " + this->sound->getFilename());
			return;
		}
		if (write1 != NULL)
		{
			memcpy(write1, &stream[0], length1);
		}
		if (write2 != NULL)
		{
			memcpy(write2, &stream[length1], length2);
		}
		this->dsBuffer->Unlock(write1, length1, write2, length2);
		if (this->sound->isStreamed())
		{
			this->bufferIndex = (this->bufferIndex + count) % STREAM_BUFFER_COUNT;
			this->bufferQueued += count;
		}
	}

	void DirectSound_Player::_copySilence(int size, int count)
	{
		void* write1 = NULL;
		void* write2 = NULL;
		unsigned long length1;
		unsigned long length2;
		int lockOffset = 0;
		if (this->sound->isStreamed())
		{
			lockOffset = this->bufferIndex * STREAM_BUFFER_SIZE;
		}
		HRESULT result = this->dsBuffer->Lock(lockOffset, size * count, &write1, &length1, &write2, &length2, 0);
		if (FAILED(result))
		{
			hlog::error(xal::logTag, "Cannot lock buffer for: " + this->sound->getFilename());
			return;
		}
		if (write1 != NULL)
		{
			memset(write1, 0, length1);
		}
		if (write2 != NULL)
		{
			memset(write2, 0, length2);
		}
		this->dsBuffer->Unlock(write1, length1, write2, length2);
		if (this->sound->isStreamed())
		{
			this->bufferIndex = (this->bufferIndex + count) % STREAM_BUFFER_COUNT;
			this->bufferQueued += count;
		}
	}

	void DirectSound_Player::_systemUpdateGain()
	{
		if (this->dsBuffer != NULL)
		{
			float gain = this->_calcGain();
			LONG value = DSBVOLUME_MIN;
			if (gain > 0.0f)
			{
				value = (LONG)(log10(gain) / 4 * (DSBVOLUME_MAX - DSBVOLUME_MIN));
			}
			this->dsBuffer->SetVolume(value);
		}
	}

	void DirectSound_Player::_systemUpdatePitch()
	{
		if (this->dsBuffer != NULL)
		{
			DWORD freq = (DWORD)(this->pitch * this->buffer->getSamplingRate());
			DWORD limit = ((DirectSound_AudioManager*)xal::mgr)->dsCaps->dwMaxSecondarySampleRate;
			this->dsBuffer->SetFrequency(hmin(freq, limit));
		}
	}

	void DirectSound_Player::_systemPlay()
	{
		if (this->dsBuffer != NULL)
		{
			this->dsBuffer->Play(0, 0, ((this->looping || this->sound->isStreamed()) ? DSBPLAY_LOOPING : 0));
		}
	}

	int DirectSound_Player::_systemStop()
	{
		int result = 0;
		if (this->dsBuffer != NULL)
		{
			this->dsBuffer->Stop();
			if (this->sound->isStreamed())
			{
				if (this->paused)
				{
					int processed = this->_getProcessedBuffersCount();
					this->bufferIndex = (this->bufferIndex + processed) % STREAM_BUFFER_COUNT;
					this->bufferCount -= processed;
					this->bufferQueued -= processed;
					result = processed * STREAM_BUFFER_SIZE;
				}
				else
				{
					this->bufferIndex = 0;
					this->bufferCount = 0;
					this->bufferQueued = 0;
					this->buffer->rewind();
				}
			}
			if (!this->paused)
			{
				this->_systemSetOffset(0); // reset buffer position 0 on stop
			}
		}
		return result;
	}

	int DirectSound_Player::_systemUpdateStream()
	{
		if (this->bufferCount == 0)
		{
			this->_stop();
			return 0;
		}
		int processed = this->_getProcessedBuffersCount();
		if (processed == 0)
		{
			return 0;
		}
		this->bufferCount = hmax(this->bufferCount - processed, 0);
		this->bufferQueued = hmax(this->bufferQueued - processed, 0);
		int count = this->_fillBuffers(this->bufferIndex, processed);
		if (count > 0)
		{
			this->_copyBuffer(this->buffer->getStream(), STREAM_BUFFER_SIZE, count);
			this->bufferCount += count;
		}
		if (!this->looping && this->bufferQueued < STREAM_BUFFER_COUNT)
		{
			count = STREAM_BUFFER_COUNT - this->bufferQueued;
			this->_copySilence(STREAM_BUFFER_SIZE, count);
		}
		if (this->bufferCount == 0)
		{
			this->_stop();
			processed = 0;
		}
		return (processed * STREAM_BUFFER_SIZE);
	}

	int DirectSound_Player::_getProcessedBuffersCount()
	{
		return ((this->_systemGetBufferPosition() / STREAM_BUFFER_SIZE +
			STREAM_BUFFER_COUNT - this->bufferIndex) % STREAM_BUFFER_COUNT);
	}

	int DirectSound_Player::_fillBuffers(int index, int count)
	{
		int size = this->buffer->load(this->looping, count * STREAM_BUFFER_SIZE);
		return (size + STREAM_BUFFER_SIZE - 1) / STREAM_BUFFER_SIZE;
	}

}
#endif<|endoftext|>


<commit_before>// Copyright (c) 2013 Joshua Beitler, Mirus contributors

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include <term/terminal.hpp>
#include <misc/printf.hpp>

#include <cpu/gdt.hpp>
#include <cpu/idt.hpp>
#include <cpu/isr.hpp>
#include <cpu/irq.hpp>

#include <hw/pit.hpp>
#include <hw/keyboard.hpp>
#include <hw/serial.hpp>

#include <sys/version.hpp>
#include <util/debug.hpp>

extern "C" void kernel_main() {
    mirus::debugger::write("mirus [0.0.0-dev]\n\n");

    // CPU funcs
	mirus::gdt::install();
    mirus::idt::install();
    mirus::isr::install();
    mirus::irq::install();

	mirus::terminal_initialize();

    // Install devices
    mirus::keyboard_install();
    mirus::timer_install();
    mirus::serial_install();

    asm volatile("sti");

    // Print version number
	// mirus::printf("mirus\r\r");

    // cause kernel panic
    int a = 6 / 0;

    // Make sure we never exit
    while (true);
}
<commit_msg>One more test<commit_after>// Copyright (c) 2013 Joshua Beitler, Mirus contributors

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#include <term/terminal.hpp>
#include <misc/printf.hpp>

#include <cpu/gdt.hpp>
#include <cpu/idt.hpp>
#include <cpu/isr.hpp>
#include <cpu/irq.hpp>

#include <hw/pit.hpp>
#include <hw/keyboard.hpp>
#include <hw/serial.hpp>

#include <sys/version.hpp>
#include <util/debug.hpp>

extern "C" void kernel_main() {
    mirus::debugger::write("mirus [0.0.0-dev]\n\n");

    // CPU funcs
	mirus::gdt::install();
    mirus::idt::install();
    mirus::isr::install();
    mirus::irq::install();

	mirus::terminal_initialize();

    // Install devices
    mirus::keyboard_install();
    mirus::timer_install();
    mirus::serial_install();

    asm volatile("sti");

    // Print version number
	mirus::printf("mirus\r\r");

    // cause kernel panic
    mirus::printf(666/0);

    // Make sure we never exit
    while (true);
}
<|endoftext|>


<commit_before>/*
    statisticsplugin.cpp

    Copyright (c) 2003-2004 by Marc Cramdal        <marc.cramdal@gmail.com>

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

#include <qfile.h>
#include <qdict.h>

#include <kgenericfactory.h>
#include <kaboutdata.h>
#include <kaction.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kdeversion.h>

#include "kopetemessagemanagerfactory.h"
#include "kopetemetacontact.h"
#include "kopeteview.h"
#include "kopetecontactlist.h"
#include "kopeteuiglobal.h"
#include "kopetemessageevent.h"
#include "kopeteonlinestatus.h"
#include "kopeteaccountmanager.h"
#include "kopeteaccount.h"

#include "statisticscontact.h"
#include "statisticsdialog.h"
#include "statisticsplugin.h"
#include "statisticsdb.h"

typedef KGenericFactory<StatisticsPlugin> StatisticsPluginFactory;

static const KAboutData aboutdata("kopete_statistics", I18N_NOOP("Statistics") , "0.1" );
K_EXPORT_COMPONENT_FACTORY( kopete_statistics, StatisticsPluginFactory( &aboutdata )  )

StatisticsPlugin::StatisticsPlugin( QObject *parent, const char *name, const QStringList &)
	: Kopete::Plugin( StatisticsPluginFactory::instance(), parent, name ),
      DCOPObject("StatisticsDCOPIface")

{
	KAction *viewMetaContactStatistics = new KAction( i18n("View &Statistics" ),
		QString::fromLatin1( "log" ), 0, this, SLOT(slotViewStatistics()),
		actionCollection(), "viewMetaContactStatistics" );
	viewMetaContactStatistics->setEnabled(Kopete::ContactList::self()->selectedMetaContacts().count() == 1);

	connect(Kopete::ChatSessionManager::self(),SIGNAL(chatSessionCreated(Kopete::ChatSession*)),
				this, SLOT(slotViewCreated(Kopete::ChatSession*)));
	connect(Kopete::ChatSessionManager::self(),SIGNAL(aboutToReceive(Kopete::Message&)),
				this, SLOT(slotAboutToReceive(Kopete::Message&)));

	connect(Kopete::ContactList::self(), SIGNAL(metaContactSelected(bool)),
		viewMetaContactStatistics, SLOT(setEnabled(bool)));
	connect(Kopete::ContactList::self(), SIGNAL(metaContactAdded(Kopete::MetaContact*)),
			this, SLOT(slotMetaContactAdded(Kopete::MetaContact*)));

	setXMLFile("statisticsui.rc");

	// Initializes the database
	m_db = new StatisticsDB();

	QPtrList<Kopete::MetaContact> list = Kopete::ContactList::self()->metaContacts();
	QPtrListIterator<Kopete::MetaContact> it( list );
	for (; it.current(); ++it)
	{
		slotMetaContactAdded(it.current());
	}
}

StatisticsPlugin::~StatisticsPlugin()
{
	map<QString, StatisticsContact*>::iterator it;
	for (it = statisticsContactMap.begin(); it != statisticsContactMap.end(); ++it)
	{
		delete it->second;
		it->second = 0;
	}
}

void StatisticsPlugin::slotAboutToReceive(Kopete::Message& m)
{
	if (!m.from()->metaContact()->metaContactId().isEmpty())
		statisticsContactMap[m.from()->metaContact()->metaContactId()]->newMessageReceived(m);
}

void StatisticsPlugin::slotViewCreated(Kopete::ChatSession* session)
{
	connect(session, SIGNAL(closing(Kopete::ChatSession*)), this, SLOT(slotViewClosed(Kopete::ChatSession*)));
}

void StatisticsPlugin::slotViewClosed(Kopete::ChatSession* session)
{
	QPtrList<Kopete::Contact> list = session->members();
	QPtrListIterator<Kopete::Contact> it( list );

	for (; it.current(); ++it)
	{
		// If this contact is not in other chat sessions
		if (!it.current()->manager() && !it.current()->metaContact()->metaContactId().isEmpty()
				   && statisticsContactMap[it.current()->metaContact()->metaContactId()])
		statisticsContactMap[it.current()->metaContact()->metaContactId()]->setIsChatWindowOpen(false);
	}
}

void StatisticsPlugin::slotViewStatistics()
{
	Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first();

	kdDebug() << k_funcinfo << "statistics - dialog :"+ m->displayName() << endl;

	if (m && !m->metaContactId().isEmpty())
	{
		(new StatisticsDialog(statisticsContactMap[m->metaContactId()], db()))->show();
	}
}

void StatisticsPlugin::slotOnlineStatusChanged(Kopete::MetaContact *contact, Kopete::OnlineStatus::StatusType status)
{
	if (statisticsContactMap[contact->metaContactId()])
			statisticsContactMap[contact->metaContactId()]->onlineStatusChanged(status);
}

void StatisticsPlugin::slotMetaContactAdded(Kopete::MetaContact *mc)
{
	connect(mc, SIGNAL(onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType)), this,
					SLOT(slotOnlineStatusChanged(Kopete::MetaContact*, Kopete::OnlineStatus::StatusType)));

	if (!mc->metaContactId().isEmpty())
		statisticsContactMap[mc->metaContactId()] = new StatisticsContact(mc, db());
}

void StatisticsPlugin::dcopStatisticsDialog(QString id)
{
	kdDebug() << k_funcinfo << "statistics - DCOP dialog :" << id << endl;

	if (statisticsContactMap[id])
	{
		(new StatisticsDialog(statisticsContactMap[id], db()))->show();
	}
}

bool StatisticsPlugin::dcopWasOnline(QString id, int timeStamp)
{
	QDateTime dt;
	dt.setTime_t(timeStamp);
	return dcopWasStatus(id, dt, Kopete::OnlineStatus::Online);
}

bool StatisticsPlugin::dcopWasOnline(QString id, QString dateTime)
{
	return dcopWasStatus(id, QDateTime::fromString(dateTime), Kopete::OnlineStatus::Online);
}

bool StatisticsPlugin::dcopWasAway(QString id, int timeStamp)
{
	QDateTime dt;
	dt.setTime_t(timeStamp);
	return dcopWasStatus(id, dt, Kopete::OnlineStatus::Away);
}

bool StatisticsPlugin::dcopWasAway(QString id, QString dateTime)
{
	return dcopWasStatus(id, QDateTime::fromString(dateTime), Kopete::OnlineStatus::Away);
}

bool StatisticsPlugin::dcopWasOffline(QString id, int timeStamp)
{
	QDateTime dt;
	dt.setTime_t(timeStamp);
	return dcopWasStatus(id, dt, Kopete::OnlineStatus::Offline);
}

bool StatisticsPlugin::dcopWasOffline(QString id, QString dateTime)
{
	return dcopWasStatus(id, QDateTime::fromString(dateTime), Kopete::OnlineStatus::Offline);
}

bool StatisticsPlugin::dcopWasStatus(QString id, QDateTime dateTime, Kopete::OnlineStatus::StatusType status)
{
	kdDebug() << k_funcinfo << "statistics - DCOP wasOnline :" << id << endl;

	if (dateTime.isValid() && statisticsContactMap[id])
	{
		return statisticsContactMap[id]->wasStatus(dateTime, status);
	}

	return false;
}

QString StatisticsPlugin::dcopStatus(QString id, int timeStamp)
{
	QDateTime dt;
	dt.setTime_t(timeStamp);
	return dcopStatus(id, dt.toString());

}

QString StatisticsPlugin::dcopStatus(QString id, QString dateTime)
{
	QDateTime dt = QDateTime::fromString(dateTime);

	if (dt.isValid() && statisticsContactMap[id])
	{
		return statisticsContactMap[id]->statusAt(dt);
	}

	return "";
}

QString StatisticsPlugin::dcopMainStatus(QString id, int timeStamp)
{
	QDateTime dt;
	dt.setTime_t(timeStamp);
	if (dt.isValid() && statisticsContactMap[id])
	{
		return statisticsContactMap[id]->mainStatusDate(dt.date());
	}

	return "";
}
#include "statisticsplugin.moc"
<commit_msg>Fix Bug 98114<commit_after>/*
    statisticsplugin.cpp

    Copyright (c) 2003-2004 by Marc Cramdal        <marc.cramdal@gmail.com>

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

#include <qfile.h>
#include <qdict.h>

#include <kgenericfactory.h>
#include <kaboutdata.h>
#include <kaction.h>
#include <kmessagebox.h>
#include <kstandarddirs.h>
#include <kdeversion.h>

#include "kopetemessagemanagerfactory.h"
#include "kopetemetacontact.h"
#include "kopeteview.h"
#include "kopetecontactlist.h"
#include "kopeteuiglobal.h"
#include "kopetemessageevent.h"
#include "kopeteonlinestatus.h"
#include "kopeteaccountmanager.h"
#include "kopeteaccount.h"

#include "statisticscontact.h"
#include "statisticsdialog.h"
#include "statisticsplugin.h"
#include "statisticsdb.h"

typedef KGenericFactory<StatisticsPlugin> StatisticsPluginFactory;

static const KAboutData aboutdata("kopete_statistics", I18N_NOOP("Statistics") , "0.1" );
K_EXPORT_COMPONENT_FACTORY( kopete_statistics, StatisticsPluginFactory( &aboutdata )  )

StatisticsPlugin::StatisticsPlugin( QObject *parent, const char *name, const QStringList &)
	: Kopete::Plugin( StatisticsPluginFactory::instance(), parent, name ),
      DCOPObject("StatisticsDCOPIface")

{
	KAction *viewMetaContactStatistics = new KAction( i18n("View &Statistics" ),
		QString::fromLatin1( "log" ), 0, this, SLOT(slotViewStatistics()),
		actionCollection(), "viewMetaContactStatistics" );
	viewMetaContactStatistics->setEnabled(Kopete::ContactList::self()->selectedMetaContacts().count() == 1);

	connect(Kopete::ChatSessionManager::self(),SIGNAL(chatSessionCreated(Kopete::ChatSession*)),
				this, SLOT(slotViewCreated(Kopete::ChatSession*)));
	connect(Kopete::ChatSessionManager::self(),SIGNAL(aboutToReceive(Kopete::Message&)),
				this, SLOT(slotAboutToReceive(Kopete::Message&)));

	connect(Kopete::ContactList::self(), SIGNAL(metaContactSelected(bool)),
		viewMetaContactStatistics, SLOT(setEnabled(bool)));
	connect(Kopete::ContactList::self(), SIGNAL(metaContactAdded(Kopete::MetaContact*)),
			this, SLOT(slotMetaContactAdded(Kopete::MetaContact*)));

	setXMLFile("statisticsui.rc");

	// Initializes the database
	m_db = new StatisticsDB();

	QPtrList<Kopete::MetaContact> list = Kopete::ContactList::self()->metaContacts();
	QPtrListIterator<Kopete::MetaContact> it( list );
	for (; it.current(); ++it)
	{
		slotMetaContactAdded(it.current());
	}
}

StatisticsPlugin::~StatisticsPlugin()
{
	map<QString, StatisticsContact*>::iterator it;
	for (it = statisticsContactMap.begin(); it != statisticsContactMap.end(); ++it)
	{
		delete it->second;
		it->second = 0;
	}
}

void StatisticsPlugin::slotAboutToReceive(Kopete::Message& m)
{
	if (!m.from()->metaContact()->metaContactId().isEmpty() && statisticsContactMap[m.from()->metaContact()->metaContactId()])
		statisticsContactMap[m.from()->metaContact()->metaContactId()]->newMessageReceived(m);
}

void StatisticsPlugin::slotViewCreated(Kopete::ChatSession* session)
{
	connect(session, SIGNAL(closing(Kopete::ChatSession*)), this, SLOT(slotViewClosed(Kopete::ChatSession*)));
}

void StatisticsPlugin::slotViewClosed(Kopete::ChatSession* session)
{
	QPtrList<Kopete::Contact> list = session->members();
	QPtrListIterator<Kopete::Contact> it( list );

	for (; it.current(); ++it)
	{
		// If this contact is not in other chat sessions
		if (!it.current()->manager() && !it.current()->metaContact()->metaContactId().isEmpty()
				   && statisticsContactMap[it.current()->metaContact()->metaContactId()])
		statisticsContactMap[it.current()->metaContact()->metaContactId()]->setIsChatWindowOpen(false);
	}
}

void StatisticsPlugin::slotViewStatistics()
{
	Kopete::MetaContact *m=Kopete::ContactList::self()->selectedMetaContacts().first();

	kdDebug() << k_funcinfo << "statistics - dialog :"+ m->displayName() << endl;

	if (m && !m->metaContactId().isEmpty())
	{
		(new StatisticsDialog(statisticsContactMap[m->metaContactId()], db()))->show();
	}
}

void StatisticsPlugin::slotOnlineStatusChanged(Kopete::MetaContact *contact, Kopete::OnlineStatus::StatusType status)
{
	if (statisticsContactMap[contact->metaContactId()])
			statisticsContactMap[contact->metaContactId()]->onlineStatusChanged(status);
}

void StatisticsPlugin::slotMetaContactAdded(Kopete::MetaContact *mc)
{
	connect(mc, SIGNAL(onlineStatusChanged( Kopete::MetaContact *, Kopete::OnlineStatus::StatusType)), this,
					SLOT(slotOnlineStatusChanged(Kopete::MetaContact*, Kopete::OnlineStatus::StatusType)));

	if (!mc->metaContactId().isEmpty())
		statisticsContactMap[mc->metaContactId()] = new StatisticsContact(mc, db());
}

void StatisticsPlugin::dcopStatisticsDialog(QString id)
{
	kdDebug() << k_funcinfo << "statistics - DCOP dialog :" << id << endl;

	if (statisticsContactMap[id])
	{
		(new StatisticsDialog(statisticsContactMap[id], db()))->show();
	}
}

bool StatisticsPlugin::dcopWasOnline(QString id, int timeStamp)
{
	QDateTime dt;
	dt.setTime_t(timeStamp);
	return dcopWasStatus(id, dt, Kopete::OnlineStatus::Online);
}

bool StatisticsPlugin::dcopWasOnline(QString id, QString dateTime)
{
	return dcopWasStatus(id, QDateTime::fromString(dateTime), Kopete::OnlineStatus::Online);
}

bool StatisticsPlugin::dcopWasAway(QString id, int timeStamp)
{
	QDateTime dt;
	dt.setTime_t(timeStamp);
	return dcopWasStatus(id, dt, Kopete::OnlineStatus::Away);
}

bool StatisticsPlugin::dcopWasAway(QString id, QString dateTime)
{
	return dcopWasStatus(id, QDateTime::fromString(dateTime), Kopete::OnlineStatus::Away);
}

bool StatisticsPlugin::dcopWasOffline(QString id, int timeStamp)
{
	QDateTime dt;
	dt.setTime_t(timeStamp);
	return dcopWasStatus(id, dt, Kopete::OnlineStatus::Offline);
}

bool StatisticsPlugin::dcopWasOffline(QString id, QString dateTime)
{
	return dcopWasStatus(id, QDateTime::fromString(dateTime), Kopete::OnlineStatus::Offline);
}

bool StatisticsPlugin::dcopWasStatus(QString id, QDateTime dateTime, Kopete::OnlineStatus::StatusType status)
{
	kdDebug() << k_funcinfo << "statistics - DCOP wasOnline :" << id << endl;

	if (dateTime.isValid() && statisticsContactMap[id])
	{
		return statisticsContactMap[id]->wasStatus(dateTime, status);
	}

	return false;
}

QString StatisticsPlugin::dcopStatus(QString id, int timeStamp)
{
	QDateTime dt;
	dt.setTime_t(timeStamp);
	return dcopStatus(id, dt.toString());

}

QString StatisticsPlugin::dcopStatus(QString id, QString dateTime)
{
	QDateTime dt = QDateTime::fromString(dateTime);

	if (dt.isValid() && statisticsContactMap[id])
	{
		return statisticsContactMap[id]->statusAt(dt);
	}

	return "";
}

QString StatisticsPlugin::dcopMainStatus(QString id, int timeStamp)
{
	QDateTime dt;
	dt.setTime_t(timeStamp);
	if (dt.isValid() && statisticsContactMap[id])
	{
		return statisticsContactMap[id]->mainStatusDate(dt.date());
	}

	return "";
}
#include "statisticsplugin.moc"
<|endoftext|>


<commit_before>/**
 * PANDA 3D SOFTWARE
 * Copyright (c) Carnegie Mellon University.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file newheader.cxx
 * @author drose
 * @date 2004-07-05
 */

#include "dtoolbase.h"

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

const char *cxx_style =
"// Filename: %s\n"
"// Created by:  %s (%s)\n"
"//\n"
"////////////////////////////////////////////////////////////////////\n"
"//\n"
"// PANDA 3D SOFTWARE\n"
"// Copyright (c) Carnegie Mellon University.  All rights reserved.\n"
"//\n"
"// All use of this software is subject to the terms of the revised BSD\n"
"// license.  You should have received a copy of this license along\n"
"// with this source code in a file named \"LICENSE.\"\n"
"//\n"
"////////////////////////////////////////////////////////////////////\n"
"\n";

const char *c_style =
"/* Filename: %s\n"
" * Created by:  %s (%s)\n"
" *\n"
" * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\n"
" *\n"
" * PANDA 3D SOFTWARE\n"
" * Copyright (c) Carnegie Mellon University.  All rights reserved.\n"
" *\n"
" * All use of this software is subject to the terms of the revised BSD\n"
" * license.  You should have received a copy of this license along\n"
" * with this source code in a file named \"LICENSE.\"\n"
" *\n"
" * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */\n"
"\n";

struct FileDef {
  const char *extension;
  const char *header;
};

FileDef file_def[] = {
  { "h", cxx_style },
  { "cxx", cxx_style },
  { "I", cxx_style },
  { "T", cxx_style },
  { "c", c_style },
  { NULL, NULL },
};

void
generate_header(const char *header, const string &filename) {
  const char *username = getenv("USER");
  if (username == NULL) {
    username = "";
  }

  static const size_t max_date_buffer = 128;
  char date_buffer[max_date_buffer];
  time_t now = time(NULL);
  strftime(date_buffer, max_date_buffer, "%d%b%y", localtime(&now));

  printf(header, filename.c_str(), username, date_buffer);
}

int
main(int argc, char *argv[]) {
  if (argc < 2) {
    cerr << "Must specify the filename to generate a header for.\n";
    exit(1);
  }

  string filename = argv[1];
  size_t dot = filename.rfind('.');
  if (dot == string::npos) {
    // No extension, no header.
    return 0;
  }

  string extension = filename.substr(dot + 1);

  size_t i = 0;
  while (file_def[i].extension != NULL) {
    if (extension == file_def[i].extension) {
      generate_header(file_def[i].header, filename);
      return 0;
    }
    i++;
  }

  // No matching extension, no problem.
  return 0;
}
<commit_msg>dtool: Delete 'newheader'<commit_after><|endoftext|>


<commit_before>#include <term/terminal.hpp>
#include <misc/printf.hpp>

#include <cpu/gdt.hpp>
#include <cpu/idt.hpp>
#include <cpu/isr.hpp>
#include <cpu/irq.hpp>

#include <hw/pit.hpp>
#include <hw/keyboard.hpp>

extern "C" void kernel_main()
{
    // install the GDT
	mirus::gdt_install();

    // install the IDT
    mirus::idt_install();

    // install ISRs
    mirus::isrs_install();

    // install IRQs
    mirus::irq_install();

    // initilize display
	mirus::terminal_initialize();

    // install keyboard
    mirus::keyboard_install();

    // install timer
    mirus::timer_install();

    asm volatile("sti");

    // print version number
	mirus::printf("mirus ");
	mirus::printf(BUILD_MAJOR);
	mirus::printf(".");
	mirus::printf(BUILD_MINOR);
	mirus::printf(".");
	mirus::printf(BUILD_NUM);
    mirus::printf("\r");
    mirus::printf("\r");

    // WHAT THE HELL
    // It seems that the keyboard won't work without this, even if the Timer is not installed.
    mirus::timer_wait(1000);

    mirus::printf("Write some stuffs: ");
}
<commit_msg>Updated kernel_main()<commit_after>#include <term/terminal.hpp>
#include <misc/printf.hpp>

#include <cpu/gdt.hpp>
#include <cpu/idt.hpp>
#include <cpu/isr.hpp>
#include <cpu/irq.hpp>

#include <hw/pit.hpp>
#include <hw/keyboard.hpp>

extern "C" void kernel_main()
{
    // install the GDT
	mirus::gdt_install();

    // install the IDT
    mirus::idt_install();

    // install ISRs
    mirus::isrs_install();

    // install IRQs
    mirus::irq_install();

    // initilize display
	mirus::terminal_initialize();

    // install keyboard
    mirus::keyboard_install();

    // install timer
    mirus::timer_install();

    asm volatile("sti");

    // print version number
	mirus::printf("mirus ");
	mirus::printf(BUILD_MAJOR);
	mirus::printf(".");
	mirus::printf(BUILD_MINOR);
	mirus::printf(".");
	mirus::printf(BUILD_NUM);
    mirus::printf("\r");
    mirus::printf("\r");

    mirus::printf("Write some stuffs: ");

    // Make sure we never exit
    while (true);

    // WHAT THE HELL
    // It seems that the keyboard won't work without this, even if the Timer is not installed.
    //mirus::timer_wait(1000);
}
<|endoftext|>


<commit_before>// Copyright 2010-2014 RethinkDB, all rights reserved.
#include "clustering/administration/main/version_check.hpp"

#include <math.h>

#include "logger.hpp"

#include "clustering/administration/metadata.hpp"
#include "extproc/http_runner.hpp"
#include "rdb_protocol/env.hpp"

namespace ql {
void dispatch_http(ql::env_t *env,
                   const http_opts_t &opts,
                   http_runner_t *runner,
                   http_result_t *res_out,
                   const ql::pb_rcheckable_t *parent);
};

version_checker_t::version_checker_t(rdb_context_t *_rdb_ctx, signal_t *_interruptor,
                                     version_checker_t::metadata_ptr_t _metadata,
                                     const std::string &_uname) :
    rdb_ctx(_rdb_ctx),
    interruptor(_interruptor),
    seen_version(),
    metadata(_metadata),
    uname(_uname) {
    rassert(rdb_ctx != NULL);
}

void version_checker_t::initial_check() {
    logINF("Beginning initial checkin");

    ql::env_t env(rdb_ctx, interruptor, std::map<std::string, ql::wire_func_t>(), nullptr);
    http_opts_t opts;
    opts.limits = env.limits();
    opts.result_format = http_result_format_t::JSON;
    opts.url = strprintf("http://update.rethinkdb.com/update_for/%s",
                         RETHINKDB_VERSION);

    http_runner_t runner(env.get_extproc_pool());
    http_result_t result;

    try {
        dispatch_http(&env, opts, &runner, &result, nullptr);
    } catch (const ql::base_exc_t &ex) {
        logWRN("Saw exception `%s` in initial checkin", ex.what());
    }

    process_result(result);
}

void version_checker_t::periodic_checkin(auto_drainer_t::lock_t) {
    logINF("Beginning periodic checkin");

    const cluster_semilattice_metadata_t snapshot = metadata->get();
    ql::env_t env(rdb_ctx, interruptor, std::map<std::string, ql::wire_func_t>(), nullptr);
    http_opts_t opts;
    opts.method = http_method_t::POST;
    opts.limits = env.limits();
    opts.result_format = http_result_format_t::JSON;
    opts.url = "http://update.rethinkdb.com/checkin";
    opts.header.push_back("Content-Type: application/x-www-form-urlencoded");
    opts.form_data["Version"] = RETHINKDB_VERSION;
    opts.form_data["Number-Of-Servers"] = snapshot.servers.servers.size();
    opts.form_data["Uname"] = uname;
    opts.form_data["Cooked-Number-Of-Tables"]
        = strprintf("%" PR_RECONSTRUCTABLE_DOUBLE,
                    cook(snapshot.rdb_namespaces->namespaces.size()));
    //opts.form_data["Cooked-Size-Of-Shards"]
    //    = strprintf("%" PR_RECONSTRUCTABLE_DOUBLE, cook(0.0)); // XXX
    logINF("server # %lu", snapshot.servers.servers.size());
    logINF("uname %s", uname.c_str());
    logINF("tables %lu", snapshot.rdb_namespaces->namespaces.size());
    logINF("databases %lu", snapshot.databases.databases.size());

    http_runner_t runner(env.get_extproc_pool());
    http_result_t result;

    try {
        dispatch_http(&env, opts, &runner, &result, nullptr);
    } catch (const ql::base_exc_t &ex) {
        logWRN("Saw exception `%s` in periodic checkin", ex.what());
    }

    process_result(result);
}

// sort of anonymize the input; specifically we want $2^(round(log_2(n)))$
double version_checker_t::cook(double n) {
    return exp2(round(log2(n)));
}

void version_checker_t::process_result(const http_result_t &result) {
    if (!result.error.empty()) {
        logWRN("Experienced protocol error attempting to check for updates: saw %s",
               result.error.c_str());
        return;
    }

    if (!result.body.has()) {
        logWRN("No body returned; aborting.");
        return;
    }

    ql::datum_t status = result.body.get_field("status", ql::NOTHROW);
    if (!status.has()) {
        logWRN("Got bizarre result from checking for updates; ignoring.");
        logDBG("Saw invalid datum %s; headers are %s",
               result.body.trunc_print().c_str(), result.header.trunc_print().c_str());
        return;
    } else if (status.get_type() != ql::datum_t::R_STR) {
        logWRN("Got malformed result from checking for updates; ignoring.");
        logDBG("Saw invalid status field %s; datum %s; headers are %s",
               status.trunc_print().c_str(), result.body.trunc_print().c_str(),
               result.header.trunc_print().c_str());
        return;
    }
    const datum_string_t &str = status.as_str();
    if (str == "ok") {
        logINF("Server up-to-date");
    } else if (str == "error") {
        ql::datum_t reason = result.body.get_field("error", ql::NOTHROW);
        if (!reason.has() || reason.get_type() != ql::datum_t::R_STR) {
            logWRN("Remote server had some kind of problem: %s",
                   result.body.trunc_print().c_str());
        } else {
            logWRN("Remote server returned error message %s when checking for updates.",
                   reason.trunc_print().c_str());
        }
    } else if (str == "need_update") {
        ql::datum_t new_version = result.body.get_field("last_version", ql::NOTHROW);
        if (!new_version.has() || new_version.get_type() != ql::datum_t::R_STR) {
            logWRN("Remote server had some kind of problem: %s",
                   result.body.trunc_print().c_str());
        } else if (seen_version != new_version.as_str()) {
            logNTC("New server version available: %s", new_version.trunc_print().c_str());
            ql::datum_t changelog = result.body.get_field("link_changelog", ql::NOTHROW);
            if (changelog.has() && changelog.get_type() == ql::datum_t::R_STR) {
                logNTC("Changelog URL: %s", changelog.trunc_print().c_str());
            }
            seen_version = new_version.as_str();
        } else {
            // already logged an update for that version, so no point
            // in spamming them.
        }
    } else {
        logWRN("Remote server gave a status code I don't understand: %s",
               result.body.trunc_print().c_str());
    }
}
<commit_msg>Don't output this by default.<commit_after>// Copyright 2010-2014 RethinkDB, all rights reserved.
#include "clustering/administration/main/version_check.hpp"

#include <math.h>

#include "logger.hpp"

#include "clustering/administration/metadata.hpp"
#include "extproc/http_runner.hpp"
#include "rdb_protocol/env.hpp"

namespace ql {
void dispatch_http(ql::env_t *env,
                   const http_opts_t &opts,
                   http_runner_t *runner,
                   http_result_t *res_out,
                   const ql::pb_rcheckable_t *parent);
};

version_checker_t::version_checker_t(rdb_context_t *_rdb_ctx, signal_t *_interruptor,
                                     version_checker_t::metadata_ptr_t _metadata,
                                     const std::string &_uname) :
    rdb_ctx(_rdb_ctx),
    interruptor(_interruptor),
    seen_version(),
    metadata(_metadata),
    uname(_uname) {
    rassert(rdb_ctx != NULL);
}

void version_checker_t::initial_check() {
    logINF("Beginning initial checkin");

    ql::env_t env(rdb_ctx, interruptor, std::map<std::string, ql::wire_func_t>(), nullptr);
    http_opts_t opts;
    opts.limits = env.limits();
    opts.result_format = http_result_format_t::JSON;
    opts.url = strprintf("http://update.rethinkdb.com/update_for/%s",
                         RETHINKDB_VERSION);

    http_runner_t runner(env.get_extproc_pool());
    http_result_t result;

    try {
        dispatch_http(&env, opts, &runner, &result, nullptr);
    } catch (const ql::base_exc_t &ex) {
        logWRN("Saw exception `%s` in initial checkin", ex.what());
    }

    process_result(result);
}

void version_checker_t::periodic_checkin(auto_drainer_t::lock_t) {
    logINF("Beginning periodic checkin");

    const cluster_semilattice_metadata_t snapshot = metadata->get();
    ql::env_t env(rdb_ctx, interruptor, std::map<std::string, ql::wire_func_t>(), nullptr);
    http_opts_t opts;
    opts.method = http_method_t::POST;
    opts.limits = env.limits();
    opts.result_format = http_result_format_t::JSON;
    opts.url = "http://update.rethinkdb.com/checkin";
    opts.header.push_back("Content-Type: application/x-www-form-urlencoded");
    opts.form_data["Version"] = RETHINKDB_VERSION;
    opts.form_data["Number-Of-Servers"] = snapshot.servers.servers.size();
    opts.form_data["Uname"] = uname;
    opts.form_data["Cooked-Number-Of-Tables"]
        = strprintf("%" PR_RECONSTRUCTABLE_DOUBLE,
                    cook(snapshot.rdb_namespaces->namespaces.size()));
    //opts.form_data["Cooked-Size-Of-Shards"]
    //    = strprintf("%" PR_RECONSTRUCTABLE_DOUBLE, cook(0.0)); // XXX
    logINF("server # %lu", snapshot.servers.servers.size());
    logINF("uname %s", uname.c_str());
    logINF("tables %lu", snapshot.rdb_namespaces->namespaces.size());
    logINF("databases %lu", snapshot.databases.databases.size());

    http_runner_t runner(env.get_extproc_pool());
    http_result_t result;

    try {
        dispatch_http(&env, opts, &runner, &result, nullptr);
    } catch (const ql::base_exc_t &ex) {
        logWRN("Saw exception `%s` in periodic checkin", ex.what());
    }

    process_result(result);
}

// sort of anonymize the input; specifically we want $2^(round(log_2(n)))$
double version_checker_t::cook(double n) {
    return exp2(round(log2(n)));
}

void version_checker_t::process_result(const http_result_t &result) {
    if (!result.error.empty()) {
        logWRN("Experienced protocol error attempting to check for updates: saw %s",
               result.error.c_str());
        return;
    }

    if (!result.body.has()) {
        logWRN("No body returned; aborting.");
        return;
    }

    ql::datum_t status = result.body.get_field("status", ql::NOTHROW);
    if (!status.has()) {
        logWRN("Got bizarre result from checking for updates; ignoring.");
        logDBG("Saw invalid datum %s; headers are %s",
               result.body.trunc_print().c_str(), result.header.trunc_print().c_str());
        return;
    } else if (status.get_type() != ql::datum_t::R_STR) {
        logWRN("Got malformed result from checking for updates; ignoring.");
        logDBG("Saw invalid status field %s; datum %s; headers are %s",
               status.trunc_print().c_str(), result.body.trunc_print().c_str(),
               result.header.trunc_print().c_str());
        return;
    }
    const datum_string_t &str = status.as_str();
    if (str == "ok") {
        logDBG("Server up-to-date");
    } else if (str == "error") {
        ql::datum_t reason = result.body.get_field("error", ql::NOTHROW);
        if (!reason.has() || reason.get_type() != ql::datum_t::R_STR) {
            logWRN("Remote server had some kind of problem: %s",
                   result.body.trunc_print().c_str());
        } else {
            logWRN("Remote server returned error message %s when checking for updates.",
                   reason.trunc_print().c_str());
        }
    } else if (str == "need_update") {
        ql::datum_t new_version = result.body.get_field("last_version", ql::NOTHROW);
        if (!new_version.has() || new_version.get_type() != ql::datum_t::R_STR) {
            logWRN("Remote server had some kind of problem: %s",
                   result.body.trunc_print().c_str());
        } else if (seen_version != new_version.as_str()) {
            logNTC("New server version available: %s", new_version.trunc_print().c_str());
            ql::datum_t changelog = result.body.get_field("link_changelog", ql::NOTHROW);
            if (changelog.has() && changelog.get_type() == ql::datum_t::R_STR) {
                logNTC("Changelog URL: %s", changelog.trunc_print().c_str());
            }
            seen_version = new_version.as_str();
        } else {
            // already logged an update for that version, so no point
            // in spamming them.
        }
    } else {
        logWRN("Remote server gave a status code I don't understand: %s",
               result.body.trunc_print().c_str());
    }
}
<|endoftext|>