<commit_before>// Copyright (c) 2011 - 2017, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/timer/Timer.h>

#include <stingraykit/diagnostics/ExecutorsProfiler.h>
#include <stingraykit/function/CancellableFunction.h>
#include <stingraykit/function/bind.h>
#include <stingraykit/function/function_name_getter.h>
#include <stingraykit/log/Logger.h>
#include <stingraykit/time/ElapsedTime.h>
#include <stingraykit/FunctionToken.h>
#include <stingraykit/TaskLifeToken.h>

#include <list>
#include <map>

namespace stingray
{

	class Timer::CallbackQueue
	{
		typedef std::list<CallbackInfoPtr>					ContainerInternal;
		typedef std::map<TimeDuration, ContainerInternal>	Container;

	private:
		Mutex			_mutex;
		Container		_container;

	public:
		typedef ContainerInternal::iterator iterator;

		inline Mutex& Sync()
		{ return _mutex; }

		inline bool IsEmpty() const
		{
			MutexLock l(_mutex);
			return _container.empty();
		}

		CallbackInfoPtr Top() const;
		void Push(const CallbackInfoPtr& ci);
		void Erase(const CallbackInfoPtr& ci);
		CallbackInfoPtr Pop();
	};

	class Timer::CallbackInfo
	{
		STINGRAYKIT_NONCOPYABLE(CallbackInfo);

		typedef function<void()>			FuncT;
		typedef CallbackQueue::iterator		QueueIterator;

	private:
		FuncT						_func;
		TimeDuration				_timeToTrigger;
		optional<TimeDuration>		_period;
		TaskLifeToken				_token;
		optional<QueueIterator>		_iterator;

	private:
		friend class CallbackQueue;

		void SetIterator(const optional<QueueIterator>& it)		{ _iterator = it; }
		const optional<QueueIterator>& GetIterator() const		{ return _iterator; }

	public:
		CallbackInfo(const FuncT& func, const TimeDuration& timeToTrigger, const optional<TimeDuration>& period, const TaskLifeToken& token)
			:	_func(func),
				_timeToTrigger(timeToTrigger),
				_period(period),
				_token(token)
		{ }

		const FuncT& GetFunc() const							{ return _func; }
		FutureExecutionTester GetExecutionTester() const 		{ return _token.GetExecutionTester(); }
		void Release()											{ _token.Release(); }

		bool IsPeriodic() const									{ return _period.is_initialized(); }
		void Restart(const TimeDuration& currentTime)
		{
			STINGRAYKIT_CHECK(_period, "CallbackInfo::Restart internal error: _period is set!");
			_timeToTrigger = currentTime + *_period;
		}
		TimeDuration GetTimeToTrigger() const					{ return _timeToTrigger; }
	};

	Timer::CallbackInfoPtr Timer::CallbackQueue::Top() const
	{
		MutexLock l(_mutex);
		if (!_container.empty())
		{
			const ContainerInternal& listForTop = _container.begin()->second;
			STINGRAYKIT_CHECK(!listForTop.empty(), "try to get callback from empty list");
			return listForTop.front();
		}
		else
			return null;
	}

	void Timer::CallbackQueue::Push(const CallbackInfoPtr& ci)
	{
		MutexLock l(_mutex);
		ContainerInternal& listToInsert = _container[ci->GetTimeToTrigger()];
		ci->SetIterator(listToInsert.insert(listToInsert.end(), ci));
	}

	void Timer::CallbackQueue::Erase(const CallbackInfoPtr& ci)
	{
		MutexLock l(_mutex);
		const optional<iterator>& it = ci->GetIterator();
		if (!it)
			return;

		TimeDuration keyToErase = ci->GetTimeToTrigger();
		ContainerInternal& listToErase = _container[keyToErase];
		listToErase.erase(*it);
		if (listToErase.empty())
			_container.erase(keyToErase);
		ci->SetIterator(null);
	}

	Timer::CallbackInfoPtr Timer::CallbackQueue::Pop()
	{
		MutexLock l(_mutex);
		STINGRAYKIT_CHECK(!_container.empty(), "popping callback from empty map");
		ContainerInternal& listToPop = _container.begin()->second;
		STINGRAYKIT_CHECK(!listToPop.empty(), "popping callback from empty list");

		CallbackInfoPtr ci = listToPop.front();
		listToPop.pop_front();
		if (listToPop.empty())
			_container.erase(ci->GetTimeToTrigger());
		ci->SetIterator(null);
		return ci;
	}

	STINGRAYKIT_DEFINE_NAMED_LOGGER(Timer);

	Timer::Timer(const std::string& timerName, const ExceptionHandler& exceptionHandler, bool profileCalls)
		:	_timerName(timerName),
			_exceptionHandler(exceptionHandler),
			_profileCalls(profileCalls),
			_alive(true),
			_queue(make_shared<CallbackQueue>()),
			_worker(make_shared<Thread>(timerName, bind(&Timer::ThreadFunc, this, not_using(_1))))
	{ }

	Timer::~Timer()
	{
		{
			MutexLock l(_queue->Sync());
			_alive = false;
			_cond.Broadcast();
		}
		_worker.reset();

		MutexLock l(_queue->Sync());
		while(!_queue->IsEmpty())
		{
			CallbackInfoPtr top = _queue->Pop();

			MutexUnlock ul(l);
			LocalExecutionGuard guard(top->GetExecutionTester());

			top.reset();
			if (guard)
			{
				s_logger.Warning() << "killing timer " << _timerName << " which still has some functions to execute";
				break;
			}
		}
	}

	Token Timer::SetTimeout(const TimeDuration& timeout, const function<void()>& func)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, null, TaskLifeToken());
		_queue->Push(ci);
		_cond.Broadcast();

		return MakeToken<FunctionToken>(bind(&Timer::RemoveTask, _queue, ci));
	}

	Token Timer::SetTimer(const TimeDuration& interval, const function<void()>& func)
	{ return SetTimer(interval, interval, func); }

	Token Timer::SetTimer(const TimeDuration& timeout, const TimeDuration& interval, const function<void()>& func)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, interval, TaskLifeToken());
		_queue->Push(ci);
		_cond.Broadcast();

		return MakeToken<FunctionToken>(bind(&Timer::RemoveTask, _queue, ci));
	}

	void Timer::AddTask(const function<void()>& task, const FutureExecutionTester& tester)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(MakeCancellableFunction(task, tester), _monotonic.Elapsed(), null, TaskLifeToken::CreateDummyTaskToken());
		_queue->Push(ci);
		_cond.Broadcast();
	}

	void Timer::RemoveTask(const CallbackQueuePtr& queue, const CallbackInfoPtr& ci)
	{
		{
			MutexLock l(queue->Sync());
			queue->Erase(ci);
		}
		ci->Release();
	}

	std::string Timer::GetProfilerMessage(const function<void()>& func)
	{ return StringBuilder() % get_function_name(func) % " in Timer '" % _timerName % "'"; }

	void Timer::ThreadFunc()
	{
		MutexLock l(_queue->Sync());

		while (_alive)
		{
			if (_queue->IsEmpty())
			{
				_cond.Wait(_queue->Sync());
				continue;
			}

			CallbackInfoPtr top = _queue->Top();
			if (top->GetTimeToTrigger() <= _monotonic.Elapsed())
			{
				_queue->Pop();

				{
					MutexUnlock ul(l);
					LocalExecutionGuard guard(top->GetExecutionTester());
					if (!guard)
					{
						top.reset();
						continue;
					}

					if (top->IsPeriodic())
						top->Restart(_monotonic.Elapsed());

					try
					{
						if (_profileCalls)
						{
							AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
							(top->GetFunc())();
						}
						else
							(top->GetFunc())();
					}
					catch(const std::exception &ex)
					{ _exceptionHandler(ex); }

					if (!top->IsPeriodic())
						top.reset();
				}

				if (top)
					_queue->Push(top);
			}
			else //top timer not triggered
			{
				const TimeDuration waitTime = top->GetTimeToTrigger() - _monotonic.Elapsed();
				top.reset();
				if (waitTime > TimeDuration())
					_cond.TimedWait(_queue->Sync(), waitTime);
			}
		}

		const TimeDuration currentTime = _monotonic.Elapsed();
		while (!_queue->IsEmpty())
		{
			CallbackInfoPtr top = _queue->Pop();

			if (top->GetTimeToTrigger() <= currentTime)
				break;

			MutexUnlock ul(l);
			LocalExecutionGuard guard(top->GetExecutionTester());
			if (!guard)
			{
				top.reset();
				continue;
			}

			try
			{
				if (_profileCalls)
				{
					AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
					(top->GetFunc())();
				}
				else
					(top->GetFunc())();
			}
			catch(const std::exception &ex)
			{ _exceptionHandler(ex); }

			top.reset();
		}
	}

	void Timer::DefaultExceptionHandler(const std::exception& ex)
	{ s_logger.Error() << "Timer func exception: " << ex; }

}
<commit_msg>Timer: avoid pushing functors from reseted tokens back to queue<commit_after>// Copyright (c) 2011 - 2017, GS Group, https://github.com/GSGroup
// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted,
// provided that the above copyright notice and this permission notice appear in all copies.
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
// WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

#include <stingraykit/timer/Timer.h>

#include <stingraykit/diagnostics/ExecutorsProfiler.h>
#include <stingraykit/function/CancellableFunction.h>
#include <stingraykit/function/bind.h>
#include <stingraykit/function/function_name_getter.h>
#include <stingraykit/log/Logger.h>
#include <stingraykit/time/ElapsedTime.h>
#include <stingraykit/FunctionToken.h>
#include <stingraykit/TaskLifeToken.h>

#include <list>
#include <map>

namespace stingray
{

	class Timer::CallbackQueue
	{
		typedef std::list<CallbackInfoPtr>					ContainerInternal;
		typedef std::map<TimeDuration, ContainerInternal>	Container;

	private:
		Mutex			_mutex;
		Container		_container;

	public:
		typedef ContainerInternal::iterator iterator;

		inline Mutex& Sync()
		{ return _mutex; }

		inline bool IsEmpty() const
		{
			MutexLock l(_mutex);
			return _container.empty();
		}

		CallbackInfoPtr Top() const;
		void Push(const CallbackInfoPtr& ci);
		void Erase(const CallbackInfoPtr& ci);
		CallbackInfoPtr Pop();
	};

	class Timer::CallbackInfo
	{
		STINGRAYKIT_NONCOPYABLE(CallbackInfo);

		typedef function<void()>			FuncT;
		typedef CallbackQueue::iterator		QueueIterator;

	private:
		FuncT						_func;
		TimeDuration				_timeToTrigger;
		optional<TimeDuration>		_period;
		TaskLifeToken				_token;

		bool						_erased;
		optional<QueueIterator>		_iterator;

	private:
		friend class CallbackQueue;

		void SetIterator(const optional<QueueIterator>& it)		{ _iterator = it; }
		const optional<QueueIterator>& GetIterator() const		{ return _iterator; }
		void SetErased()										{ _erased = true; }
		bool IsErased() const									{ return _erased; }

	public:
		CallbackInfo(const FuncT& func, const TimeDuration& timeToTrigger, const optional<TimeDuration>& period, const TaskLifeToken& token)
			:	_func(func),
				_timeToTrigger(timeToTrigger),
				_period(period),
				_token(token),
				_erased(false)
		{ }

		const FuncT& GetFunc() const							{ return _func; }
		FutureExecutionTester GetExecutionTester() const 		{ return _token.GetExecutionTester(); }
		void Release()											{ _token.Release(); }

		bool IsPeriodic() const									{ return _period.is_initialized(); }
		void Restart(const TimeDuration& currentTime)
		{
			STINGRAYKIT_CHECK(_period, "CallbackInfo::Restart internal error: _period is set!");
			_timeToTrigger = currentTime + *_period;
		}
		TimeDuration GetTimeToTrigger() const					{ return _timeToTrigger; }
	};

	Timer::CallbackInfoPtr Timer::CallbackQueue::Top() const
	{
		MutexLock l(_mutex);
		if (!_container.empty())
		{
			const ContainerInternal& listForTop = _container.begin()->second;
			STINGRAYKIT_CHECK(!listForTop.empty(), "try to get callback from empty list");
			return listForTop.front();
		}
		else
			return null;
	}

	void Timer::CallbackQueue::Push(const CallbackInfoPtr& ci)
	{
		MutexLock l(_mutex);
		if (ci->IsErased())
			return;

		ContainerInternal& listToInsert = _container[ci->GetTimeToTrigger()];
		ci->SetIterator(listToInsert.insert(listToInsert.end(), ci));
	}

	void Timer::CallbackQueue::Erase(const CallbackInfoPtr& ci)
	{
		MutexLock l(_mutex);
		ci->SetErased();

		const optional<iterator>& it = ci->GetIterator();
		if (!it)
			return;

		TimeDuration keyToErase = ci->GetTimeToTrigger();
		ContainerInternal& listToErase = _container[keyToErase];
		listToErase.erase(*it);
		if (listToErase.empty())
			_container.erase(keyToErase);
		ci->SetIterator(null);
	}

	Timer::CallbackInfoPtr Timer::CallbackQueue::Pop()
	{
		MutexLock l(_mutex);
		STINGRAYKIT_CHECK(!_container.empty(), "popping callback from empty map");
		ContainerInternal& listToPop = _container.begin()->second;
		STINGRAYKIT_CHECK(!listToPop.empty(), "popping callback from empty list");

		CallbackInfoPtr ci = listToPop.front();
		listToPop.pop_front();
		if (listToPop.empty())
			_container.erase(ci->GetTimeToTrigger());
		ci->SetIterator(null);
		return ci;
	}

	STINGRAYKIT_DEFINE_NAMED_LOGGER(Timer);

	Timer::Timer(const std::string& timerName, const ExceptionHandler& exceptionHandler, bool profileCalls)
		:	_timerName(timerName),
			_exceptionHandler(exceptionHandler),
			_profileCalls(profileCalls),
			_alive(true),
			_queue(make_shared<CallbackQueue>()),
			_worker(make_shared<Thread>(timerName, bind(&Timer::ThreadFunc, this, not_using(_1))))
	{ }

	Timer::~Timer()
	{
		{
			MutexLock l(_queue->Sync());
			_alive = false;
			_cond.Broadcast();
		}
		_worker.reset();

		MutexLock l(_queue->Sync());
		while(!_queue->IsEmpty())
		{
			CallbackInfoPtr top = _queue->Pop();

			MutexUnlock ul(l);
			LocalExecutionGuard guard(top->GetExecutionTester());

			top.reset();
			if (guard)
			{
				s_logger.Warning() << "killing timer " << _timerName << " which still has some functions to execute";
				break;
			}
		}
	}

	Token Timer::SetTimeout(const TimeDuration& timeout, const function<void()>& func)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, null, TaskLifeToken());
		_queue->Push(ci);
		_cond.Broadcast();

		return MakeToken<FunctionToken>(bind(&Timer::RemoveTask, _queue, ci));
	}

	Token Timer::SetTimer(const TimeDuration& interval, const function<void()>& func)
	{ return SetTimer(interval, interval, func); }

	Token Timer::SetTimer(const TimeDuration& timeout, const TimeDuration& interval, const function<void()>& func)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(func, _monotonic.Elapsed() + timeout, interval, TaskLifeToken());
		_queue->Push(ci);
		_cond.Broadcast();

		return MakeToken<FunctionToken>(bind(&Timer::RemoveTask, _queue, ci));
	}

	void Timer::AddTask(const function<void()>& task, const FutureExecutionTester& tester)
	{
		MutexLock l(_queue->Sync());

		CallbackInfoPtr ci = make_shared<CallbackInfo>(MakeCancellableFunction(task, tester), _monotonic.Elapsed(), null, TaskLifeToken::CreateDummyTaskToken());
		_queue->Push(ci);
		_cond.Broadcast();
	}

	void Timer::RemoveTask(const CallbackQueuePtr& queue, const CallbackInfoPtr& ci)
	{
		{
			MutexLock l(queue->Sync());
			queue->Erase(ci);
		}
		ci->Release();
	}

	std::string Timer::GetProfilerMessage(const function<void()>& func)
	{ return StringBuilder() % get_function_name(func) % " in Timer '" % _timerName % "'"; }

	void Timer::ThreadFunc()
	{
		MutexLock l(_queue->Sync());

		while (_alive)
		{
			if (_queue->IsEmpty())
			{
				_cond.Wait(_queue->Sync());
				continue;
			}

			CallbackInfoPtr top = _queue->Top();
			if (top->GetTimeToTrigger() <= _monotonic.Elapsed())
			{
				_queue->Pop();

				{
					MutexUnlock ul(l);
					LocalExecutionGuard guard(top->GetExecutionTester());
					if (!guard)
					{
						top.reset();
						continue;
					}

					if (top->IsPeriodic())
						top->Restart(_monotonic.Elapsed());

					try
					{
						if (_profileCalls)
						{
							AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
							(top->GetFunc())();
						}
						else
							(top->GetFunc())();
					}
					catch(const std::exception &ex)
					{ _exceptionHandler(ex); }

					if (!top->IsPeriodic())
						top.reset();
				}

				if (top)
					_queue->Push(top);
			}
			else //top timer not triggered
			{
				const TimeDuration waitTime = top->GetTimeToTrigger() - _monotonic.Elapsed();
				top.reset();
				if (waitTime > TimeDuration())
					_cond.TimedWait(_queue->Sync(), waitTime);
			}
		}

		const TimeDuration currentTime = _monotonic.Elapsed();
		while (!_queue->IsEmpty())
		{
			CallbackInfoPtr top = _queue->Pop();

			if (top->GetTimeToTrigger() <= currentTime)
				break;

			MutexUnlock ul(l);
			LocalExecutionGuard guard(top->GetExecutionTester());
			if (!guard)
			{
				top.reset();
				continue;
			}

			try
			{
				if (_profileCalls)
				{
					AsyncProfiler::Session profiler_session(ExecutorsProfiler::Instance().GetProfiler(), bind(&Timer::GetProfilerMessage, this, ref(top->GetFunc())), 10000, AsyncProfiler::Session::NameGetterTag());
					(top->GetFunc())();
				}
				else
					(top->GetFunc())();
			}
			catch(const std::exception &ex)
			{ _exceptionHandler(ex); }

			top.reset();
		}
	}

	void Timer::DefaultExceptionHandler(const std::exception& ex)
	{ s_logger.Error() << "Timer func exception: " << ex; }

}
<|endoftext|>


<commit_before>#include "sqlite_wrapper.hpp"

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>

#include <fstream>
#include <memory>
#include <string>
#include <vector>

#include "constant_mappings.hpp"
#include "utils/load_table.hpp"

namespace opossum {

SQLiteWrapper::SQLiteWrapper() {
  int rc = sqlite3_open(":memory:", &_db);

  if (rc != SQLITE_OK) {
    sqlite3_close(_db);
    throw std::runtime_error("Cannot open database: " + std::string(sqlite3_errmsg(_db)) + "\n");
  }
}

SQLiteWrapper::~SQLiteWrapper() { sqlite3_close(_db); }

void SQLiteWrapper::create_table_from_tbl(const std::string& file, const std::string& table_name) {
  char* err_msg;
  std::ifstream infile(file);
  Assert(infile.is_open(), "SQLiteWrapper: Could not find file " + file);

  std::string line;
  std::getline(infile, line);
  std::vector<std::string> col_names = _split<std::string>(line, '|');
  std::getline(infile, line);
  std::vector<std::string> col_types;

  for (const std::string& type : _split<std::string>(line, '|')) {
    std::string actual_type = _split<std::string>(type, '_')[0];
    if (actual_type == "int" || actual_type == "long") {
      col_types.push_back("INT");
    } else if (actual_type == "float" || actual_type == "double") {
      col_types.push_back("REAL");
    } else if (actual_type == "string") {
      col_types.push_back("TEXT");
    } else {
      DebugAssert(false, "SQLiteWrapper: column type " + type + " not supported.");
    }
  }

  std::stringstream query;
  query << "CREATE TABLE " << table_name << "(";
  for (size_t i = 0; i < col_names.size(); i++) {
    query << col_names[i] << " " << col_types[i];

    if ((i + 1) < col_names.size()) {
      query << ", ";
    }
  }
  query << ");";

  while (std::getline(infile, line)) {
    query << "INSERT INTO " << table_name << " VALUES (";
    std::vector<std::string> values = _split<std::string>(line, '|');
    for (size_t i = 0; i < values.size(); i++) {
      if (col_types[i] == "TEXT") {
        query << "'" << values[i] << "'";
      } else {
        query << values[i];
      }

      if ((i + 1) < values.size()) {
        query << ", ";
      }
    }
    query << ");";
  }

  int rc = sqlite3_exec(_db, query.str().c_str(), 0, 0, &err_msg);

  if (rc != SQLITE_OK) {
    auto msg = std::string(err_msg);
    sqlite3_free(err_msg);
    sqlite3_close(_db);
    throw std::runtime_error("Failed to create table. SQL error: " + msg + "\n");
  }
}

std::shared_ptr<Table> SQLiteWrapper::execute_query(const std::string& sql_query) {
  sqlite3_stmt* result_row;

  auto result_table = std::make_shared<Table>();

  std::vector<std::string> queries;
  boost::algorithm::split(queries, sql_query, boost::is_any_of(";"));

  queries.erase(std::remove_if(queries.begin(), queries.end(), [](std::string const& query) { return query.empty(); }),
                queries.end());

  // We need to split the queries such that we only create columns/add rows from the final SELECT query
  std::vector<std::string> queries_before_select(queries.begin(), queries.end() - 1);
  std::string select_query = queries.back();

  int rc;
  for (const auto& query : queries_before_select) {
    rc = sqlite3_prepare_v2(_db, query.c_str(), -1, &result_row, 0);

    if (rc != SQLITE_OK) {
      sqlite3_finalize(result_row);
      sqlite3_close(_db);
      throw std::runtime_error("Failed to execute query \"" + query + "\": " + std::string(sqlite3_errmsg(_db)) + "\n");
    }

    while ((rc = sqlite3_step(result_row)) != SQLITE_DONE) {
    }
  }

  rc = sqlite3_prepare_v2(_db, select_query.c_str(), -1, &result_row, 0);

  if (rc != SQLITE_OK) {
    auto error_message = "Failed to execute query \"" + select_query + "\": " + std::string(sqlite3_errmsg(_db));
    sqlite3_finalize(result_row);
    sqlite3_close(_db);
    throw std::runtime_error(error_message);
  }

  _create_columns(result_table, result_row, sqlite3_column_count(result_row));

  sqlite3_reset(result_row);

  while ((rc = sqlite3_step(result_row)) == SQLITE_ROW) {
    _add_row(result_table, result_row, sqlite3_column_count(result_row));
  }

  sqlite3_finalize(result_row);
  return result_table;
}

void SQLiteWrapper::_create_columns(std::shared_ptr<Table> table, sqlite3_stmt* result_row, int column_count) {
  std::vector<bool> col_nullable(column_count, false);
  std::vector<std::string> col_types(column_count, "");
  std::vector<std::string> col_names(column_count, "");

  bool no_result = true;
  int rc;
  while ((rc = sqlite3_step(result_row)) == SQLITE_ROW) {
    for (int i = 0; i < column_count; ++i) {
      if (no_result) {
        col_names[i] = sqlite3_column_name(result_row, i);
      }

      switch (sqlite3_column_type(result_row, i)) {
        case SQLITE_INTEGER: {
          col_types[i] = "int";
          break;
        }

        case SQLITE_FLOAT: {
          col_types[i] = "double";
          break;
        }

        case SQLITE_TEXT: {
          col_types[i] = "string";
          break;
        }

        case SQLITE_NULL: {
          col_nullable[i] = true;
          break;
        }

        case SQLITE_BLOB:
        default: {
          sqlite3_finalize(result_row);
          sqlite3_close(_db);
          throw std::runtime_error("Column type not supported.");
        }
      }
    }
    no_result = false;
  }

  if (!no_result) {
    for (int i = 0; i < column_count; ++i) {
      if (col_types[i].empty()) {
        // Hyrise does not have explicit NULL columns
        col_types[i] = "int";
      }

      const auto data_type = data_type_to_string.right.at(col_types[i]);
      table->add_column(col_names[i], data_type, col_nullable[i]);
    }
  }
}

void SQLiteWrapper::_add_row(std::shared_ptr<Table> table, sqlite3_stmt* result_row, int column_count) {
  std::vector<AllTypeVariant> row;

  for (int i = 0; i < column_count; ++i) {
    switch (sqlite3_column_type(result_row, i)) {
      case SQLITE_INTEGER: {
        row.push_back(AllTypeVariant{sqlite3_column_int(result_row, i)});
        break;
      }

      case SQLITE_FLOAT: {
        row.push_back(AllTypeVariant{sqlite3_column_double(result_row, i)});
        break;
      }

      case SQLITE_TEXT: {
        row.push_back(AllTypeVariant{std::string(reinterpret_cast<const char*>(sqlite3_column_text(result_row, i)))});
        break;
      }

      case SQLITE_NULL: {
        row.push_back(NULL_VALUE);
        break;
      }

      case SQLITE_BLOB:
      default: {
        sqlite3_finalize(result_row);
        sqlite3_close(_db);
        throw std::runtime_error("Column type not supported.");
      }
    }
  }

  table->append(row);
}

}  // namespace opossum
<commit_msg>Fix SQLiteWrapper not interpreting 'null' correctly in a string_null column of .tbl file. (#574)<commit_after>#include "sqlite_wrapper.hpp"

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>

#include <fstream>
#include <memory>
#include <string>
#include <vector>

#include "constant_mappings.hpp"
#include "utils/load_table.hpp"

namespace opossum {

SQLiteWrapper::SQLiteWrapper() {
  int rc = sqlite3_open(":memory:", &_db);

  if (rc != SQLITE_OK) {
    sqlite3_close(_db);
    throw std::runtime_error("Cannot open database: " + std::string(sqlite3_errmsg(_db)) + "\n");
  }
}

SQLiteWrapper::~SQLiteWrapper() { sqlite3_close(_db); }

void SQLiteWrapper::create_table_from_tbl(const std::string& file, const std::string& table_name) {
  char* err_msg;
  std::ifstream infile(file);
  Assert(infile.is_open(), "SQLiteWrapper: Could not find file " + file);

  std::string line;
  std::getline(infile, line);
  std::vector<std::string> col_names = _split<std::string>(line, '|');
  std::getline(infile, line);
  std::vector<std::string> col_types;

  for (const std::string& type : _split<std::string>(line, '|')) {
    std::string actual_type = _split<std::string>(type, '_')[0];
    if (actual_type == "int" || actual_type == "long") {
      col_types.push_back("INT");
    } else if (actual_type == "float" || actual_type == "double") {
      col_types.push_back("REAL");
    } else if (actual_type == "string") {
      col_types.push_back("TEXT");
    } else {
      DebugAssert(false, "SQLiteWrapper: column type " + type + " not supported.");
    }
  }

  std::stringstream query;
  query << "CREATE TABLE " << table_name << "(";
  for (size_t i = 0; i < col_names.size(); i++) {
    query << col_names[i] << " " << col_types[i];

    if ((i + 1) < col_names.size()) {
      query << ", ";
    }
  }
  query << ");";

  while (std::getline(infile, line)) {
    query << "INSERT INTO " << table_name << " VALUES (";
    std::vector<std::string> values = _split<std::string>(line, '|');
    for (size_t i = 0; i < values.size(); i++) {
      if (col_types[i] == "TEXT" && values[i] != "null") {
        query << "'" << values[i] << "'";
      } else {
        query << values[i];
      }

      if ((i + 1) < values.size()) {
        query << ", ";
      }
    }
    query << ");";
  }

  int rc = sqlite3_exec(_db, query.str().c_str(), 0, 0, &err_msg);

  if (rc != SQLITE_OK) {
    auto msg = std::string(err_msg);
    sqlite3_free(err_msg);
    sqlite3_close(_db);
    throw std::runtime_error("Failed to create table. SQL error: " + msg + "\n");
  }
}

std::shared_ptr<Table> SQLiteWrapper::execute_query(const std::string& sql_query) {
  sqlite3_stmt* result_row;

  auto result_table = std::make_shared<Table>();

  std::vector<std::string> queries;
  boost::algorithm::split(queries, sql_query, boost::is_any_of(";"));

  queries.erase(std::remove_if(queries.begin(), queries.end(), [](std::string const& query) { return query.empty(); }),
                queries.end());

  // We need to split the queries such that we only create columns/add rows from the final SELECT query
  std::vector<std::string> queries_before_select(queries.begin(), queries.end() - 1);
  std::string select_query = queries.back();

  int rc;
  for (const auto& query : queries_before_select) {
    rc = sqlite3_prepare_v2(_db, query.c_str(), -1, &result_row, 0);

    if (rc != SQLITE_OK) {
      sqlite3_finalize(result_row);
      sqlite3_close(_db);
      throw std::runtime_error("Failed to execute query \"" + query + "\": " + std::string(sqlite3_errmsg(_db)) + "\n");
    }

    while ((rc = sqlite3_step(result_row)) != SQLITE_DONE) {
    }
  }

  rc = sqlite3_prepare_v2(_db, select_query.c_str(), -1, &result_row, 0);

  if (rc != SQLITE_OK) {
    auto error_message = "Failed to execute query \"" + select_query + "\": " + std::string(sqlite3_errmsg(_db));
    sqlite3_finalize(result_row);
    sqlite3_close(_db);
    throw std::runtime_error(error_message);
  }

  _create_columns(result_table, result_row, sqlite3_column_count(result_row));

  sqlite3_reset(result_row);

  while ((rc = sqlite3_step(result_row)) == SQLITE_ROW) {
    _add_row(result_table, result_row, sqlite3_column_count(result_row));
  }

  sqlite3_finalize(result_row);
  return result_table;
}

void SQLiteWrapper::_create_columns(std::shared_ptr<Table> table, sqlite3_stmt* result_row, int column_count) {
  std::vector<bool> col_nullable(column_count, false);
  std::vector<std::string> col_types(column_count, "");
  std::vector<std::string> col_names(column_count, "");

  bool no_result = true;
  int rc;
  while ((rc = sqlite3_step(result_row)) == SQLITE_ROW) {
    for (int i = 0; i < column_count; ++i) {
      if (no_result) {
        col_names[i] = sqlite3_column_name(result_row, i);
      }

      switch (sqlite3_column_type(result_row, i)) {
        case SQLITE_INTEGER: {
          col_types[i] = "int";
          break;
        }

        case SQLITE_FLOAT: {
          col_types[i] = "double";
          break;
        }

        case SQLITE_TEXT: {
          col_types[i] = "string";
          break;
        }

        case SQLITE_NULL: {
          col_nullable[i] = true;
          break;
        }

        case SQLITE_BLOB:
        default: {
          sqlite3_finalize(result_row);
          sqlite3_close(_db);
          throw std::runtime_error("Column type not supported.");
        }
      }
    }
    no_result = false;
  }

  if (!no_result) {
    for (int i = 0; i < column_count; ++i) {
      if (col_types[i].empty()) {
        // Hyrise does not have explicit NULL columns
        col_types[i] = "int";
      }

      const auto data_type = data_type_to_string.right.at(col_types[i]);
      table->add_column(col_names[i], data_type, col_nullable[i]);
    }
  }
}

void SQLiteWrapper::_add_row(std::shared_ptr<Table> table, sqlite3_stmt* result_row, int column_count) {
  std::vector<AllTypeVariant> row;

  for (int i = 0; i < column_count; ++i) {
    switch (sqlite3_column_type(result_row, i)) {
      case SQLITE_INTEGER: {
        row.push_back(AllTypeVariant{sqlite3_column_int(result_row, i)});
        break;
      }

      case SQLITE_FLOAT: {
        row.push_back(AllTypeVariant{sqlite3_column_double(result_row, i)});
        break;
      }

      case SQLITE_TEXT: {
        row.push_back(AllTypeVariant{std::string(reinterpret_cast<const char*>(sqlite3_column_text(result_row, i)))});
        break;
      }

      case SQLITE_NULL: {
        row.push_back(NULL_VALUE);
        break;
      }

      case SQLITE_BLOB:
      default: {
        sqlite3_finalize(result_row);
        sqlite3_close(_db);
        throw std::runtime_error("Column type not supported.");
      }
    }
  }

  table->append(row);
}

}  // namespace opossum
<|endoftext|>


<commit_before>/*
 * irods_server_properties.cpp
 *
 *  Created on: Jan 15, 2014
 *      Author: adt
 */

#include "irods_server_properties.hpp"
#include "irods_get_full_path_for_config_file.hpp"

#include "rods.hpp"
#include "irods_log.hpp"

#include "readServerConfig.hpp"
#include "initServer.hpp"

#include <string>
#include <algorithm>

#define BUF_LEN 500

namespace irods {

// Access method for singleton
server_properties& server_properties::getInstance() {
    static server_properties instance;
    return instance;
}

error server_properties::capture_if_needed() {
    error result = SUCCESS();
    if ( !captured_ ) {
        result = capture();
    }
    return result;
}

// Read server.config and fill server_properties::properties
error server_properties::capture() {
    error result = SUCCESS();
    std::string prop_name, prop_setting; // property name and setting

    FILE *fptr;
    char buf[BUF_LEN];
    char *fchar;
    int len;
    char *key;

    char DBKey[MAX_PASSWORD_LEN], DBPassword[MAX_PASSWORD_LEN];
    memset( &DBKey, '\0', MAX_PASSWORD_LEN );
    memset( &DBPassword, '\0', MAX_PASSWORD_LEN );

    std::string cfg_file;
    error ret = irods::get_full_path_for_config_file( SERVER_CONFIG_FILE, cfg_file );
    if ( !ret.ok() ) {
        return PASS( ret );
    }

    fptr = fopen( cfg_file.c_str(), "r" );

    if ( fptr == NULL ) {
        rodsLog( LOG_DEBUG,
                 "Cannot open SERVER_CONFIG_FILE file %s. errno = %d\n",
                 cfg_file.c_str(), errno );
        return ERROR( SYS_CONFIG_FILE_ERR, "server.config file error" );
    }

    buf[BUF_LEN - 1] = '\0';
    fchar = fgets( buf, BUF_LEN - 1, fptr );
    for ( ; fchar != '\0'; ) {
        if ( buf[0] == '#' || buf[0] == '/' ) {
            buf[0] = '\0'; /* Comment line, ignore */
        }

        /**
         * Parsing of server configuration settings
         */
        key = strstr( buf, DB_PASSWORD_KW );
        if ( key != NULL ) {
            len = strlen( DB_PASSWORD_KW );

            // Store password in temporary string
            snprintf( DBPassword, sizeof( DBPassword ), "%s", findNextTokenAndTerm( key + len ) );

        } // DB_PASSWORD_KW

        key = strstr( buf, DB_KEY_KW );
        if ( key != NULL ) {
            len = strlen( DB_KEY_KW );

            // Store key in temporary string
            strncpy( DBKey, findNextTokenAndTerm( key + len ), MAX_PASSWORD_LEN );

        } // DB_KEY_KW

        // =-=-=-=-=-=-=-
        // PAM configuration - init PAM values
        result = properties.set<bool>( PAM_NO_EXTEND_KW, false );
        result = properties.set<size_t>( PAM_PW_LEN_KW, 20 );

        key = strstr( buf, DB_USERNAME_KW );
        if ( key != NULL ) {
            len = strlen( DB_USERNAME_KW );

            // Set property name and setting
            prop_name.assign( DB_USERNAME_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG1, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // DB_USERNAME_KW

        // =-=-=-=-=-=-=-
        // PAM configuration - init PAM values
        result = properties.set<bool>( PAM_NO_EXTEND_KW, false );
        result = properties.set<size_t>( PAM_PW_LEN_KW, 20 );

        prop_setting.assign( "121" );
        result = properties.set<std::string>( PAM_PW_MIN_TIME_KW, prop_setting );

        prop_setting.assign( "1209600" );
        result = properties.set<std::string>( PAM_PW_MAX_TIME_KW, prop_setting );
        // init PAM values

        key = strstr( buf, PAM_PW_LEN_KW );
        if ( key != NULL ) {
            len = strlen( PAM_PW_LEN_KW );

            // Set property name and setting
            prop_name.assign( PAM_PW_LEN_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<size_t>( prop_name, atoi( prop_setting.c_str() ) );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // PAM_PW_LEN_KW

        key = strstr( buf, PAM_NO_EXTEND_KW );
        if ( key != NULL ) {
            len = strlen( PAM_NO_EXTEND_KW );

            // Set property name and setting
            prop_name.assign( PAM_NO_EXTEND_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            std::transform( prop_setting.begin(), prop_setting.end(), prop_setting.begin(), ::tolower );
            if ( prop_setting == "true" ) {
                result = properties.set<bool>( PAM_NO_EXTEND_KW, true );
            }
            else {
                result = properties.set<bool>( PAM_NO_EXTEND_KW, false );
            }

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // PAM_NO_EXTEND_KW

        key = strstr( buf, PAM_PW_MIN_TIME_KW );
        if ( key != NULL ) {
            len = strlen( PAM_PW_MIN_TIME_KW );

            // Set property name and setting
            prop_name.assign( PAM_PW_MIN_TIME_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // PAM_PW_MIN_TIME_KW

        key = strstr( buf, PAM_PW_MAX_TIME_KW );
        if ( key != NULL ) {
            len = strlen( PAM_PW_MAX_TIME_KW );

            // Set property name and setting
            prop_name.assign( PAM_PW_MAX_TIME_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // PAM_PW_MAX_TIME_KW

        key = strstr( buf, RUN_SERVER_AS_ROOT_KW );
        if ( key != NULL ) {
            len = strlen( RUN_SERVER_AS_ROOT_KW );

            // Set property name and setting
            prop_name.assign( RUN_SERVER_AS_ROOT_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            std::transform( prop_setting.begin(), prop_setting.end(), prop_setting.begin(), ::tolower );
            if ( prop_setting == "true" ) {
                result = properties.set<bool>( RUN_SERVER_AS_ROOT_KW, true );
            }
            else {
                result = properties.set<bool>( RUN_SERVER_AS_ROOT_KW, false );
            }

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // RUN_SERVER_AS_ROOT_KW

        key = strstr( buf, DEF_DIR_MODE_KW );
        if ( key != NULL ) {
            len = strlen( DEF_DIR_MODE_KW );

            // Set property name and setting
            prop_name.assign( DEF_DIR_MODE_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<int>( prop_name, strtol( prop_setting.c_str(), 0, 0 ) );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // DEF_DIR_MODE_KW

        key = strstr( buf, DEF_FILE_MODE_KW );
        if ( key != NULL ) {
            len = strlen( DEF_FILE_MODE_KW );

            // Set property name and setting
            prop_name.assign( DEF_FILE_MODE_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<int>( prop_name, strtol( prop_setting.c_str(), 0, 0 ) );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // DEF_FILE_MODE_KW

        key = strstr( buf, CATALOG_DATABASE_TYPE_KW );
        if ( key != NULL ) {
            len = strlen( CATALOG_DATABASE_TYPE_KW );

            // Set property name and setting
            prop_name.assign( CATALOG_DATABASE_TYPE_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // CATALOG_DATABASE_TYPE_KW

        key = strstr( buf, KERBEROS_NAME_KW );
        if ( key != NULL ) {
            len = strlen( KERBEROS_NAME_KW );
            // Set property name and setting
            prop_name.assign( KERBEROS_NAME_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );
            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );
            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // KERBEROS_NAME_KW

        key = strstr( buf, KERBEROS_KEYTAB_KW );
        if ( key != NULL ) {
            len = strlen( KERBEROS_KEYTAB_KW );
            // Set property name and setting
            prop_name.assign( KERBEROS_KEYTAB_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );
            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );
            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );

            // Now set the appropriate kerberos environment variable
            setenv( "KRB5_KTNAME", prop_setting.c_str(), 1 );

        } // KERBEROS_KEYTAB_KW

        key = strstr( buf, DEFAULT_HASH_SCHEME_KW );
        if ( key != NULL ) {
            len = strlen( DEFAULT_HASH_SCHEME_KW );

            // Set property name and setting
            prop_name.assign( DEFAULT_HASH_SCHEME_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );
            std::transform(
                prop_setting.begin(),
                prop_setting.end(),
                prop_setting.begin(),
                ::tolower );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // DEFAULT_HASH_SCHEME_KW

        key = strstr( buf, MATCH_HASH_POLICY_KW );
        if ( key != NULL ) {
            len = strlen( MATCH_HASH_POLICY_KW );

            // Set property name and setting
            prop_name.assign( MATCH_HASH_POLICY_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );
            std::transform(
                prop_setting.begin(),
                prop_setting.end(),
                prop_setting.begin(),
                ::tolower );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // MATCH_HASH_POLICY_KW

        key = strstr( buf, LOCAL_ZONE_SID_KW );
        if ( key != NULL ) {
            len = strlen( LOCAL_ZONE_SID_KW );

            // Set property name and setting
            prop_name.assign( LOCAL_ZONE_SID_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // LOCAL_ZONE_SID_KW

        key = strstr( buf, REMOTE_ZONE_SID_KW );
        if ( key != NULL ) {
            result = SUCCESS();
            len = strlen( REMOTE_ZONE_SID_KW );

            // Set property name and setting
            prop_name.assign( REMOTE_ZONE_SID_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );
            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );

            // Update properties table
            std::vector<std::string> rem_sids;
            if( properties.has_entry( prop_name ) ) {
                result = properties.get< std::vector< std::string > >( prop_name, rem_sids );
                if( result.ok() ) {
                    rem_sids.push_back( prop_setting );
                }
            }

            if( result.ok() ) {
                rem_sids.push_back( prop_setting );
                result = properties.set< std::vector< std::string > >( prop_name, rem_sids );
            } else {
                irods::log( PASS( result ) );
            }

        } // REMOTE_ZONE_SID_KW

        key = strstr( buf, AGENT_KEY_KW.c_str() );
        if ( key != NULL ) {
            len = strlen( AGENT_KEY_KW.c_str() );
            // Set property name and setting
            prop_name.assign( AGENT_KEY_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            if ( 32 != prop_setting.size() )
            {
                rodsLog( LOG_ERROR,
                         "%s field in server.config must be 32 characters in length (currently %d characters in length).",
                         prop_name.c_str(), prop_setting.size() );
                fclose( fptr );
                return ERROR( SYS_CONFIG_FILE_ERR, "server.config file error" );
            }

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

        } // AGENT_KEY_KW

        fchar = fgets( buf, BUF_LEN - 1, fptr );

    } // for ( ; fchar != '\0'; )

    fclose( fptr );

    // unscramble password
    if ( strlen( DBKey ) > 0 && strlen( DBPassword ) > 0 ) {
        char sPassword[MAX_PASSWORD_LEN + 10];
        strncpy( sPassword, DBPassword, MAX_PASSWORD_LEN );
        obfDecodeByKey( sPassword, DBKey, DBPassword );
        memset( sPassword, 0, MAX_PASSWORD_LEN );
    }

    // store password and key in server properties
    prop_name.assign( DB_PASSWORD_KW );
    prop_setting.assign( DBPassword );
    result = properties.set<std::string>( prop_name, prop_setting );
    rodsLog( LOG_DEBUG1, "%s=%s", prop_name.c_str(), prop_setting.c_str() );

    prop_name.assign( DB_KEY_KW );
    prop_setting.assign( DBKey );
    result = properties.set<std::string>( prop_name, prop_setting );
    rodsLog( LOG_DEBUG1, "%s=%s", prop_name.c_str(), prop_setting.c_str() );

    // set the captured flag so we no its already been captured
    captured_ = true;

    return result;

} // server_properties::capture()

} // namespace irods

<commit_msg>[#2212] CID25621:<commit_after>/*
 * irods_server_properties.cpp
 *
 *  Created on: Jan 15, 2014
 *      Author: adt
 */

#include "irods_server_properties.hpp"
#include "irods_get_full_path_for_config_file.hpp"

#include "rods.hpp"
#include "irods_log.hpp"

#include "readServerConfig.hpp"
#include "initServer.hpp"

#include <string>
#include <algorithm>

#define BUF_LEN 500

namespace irods {

// Access method for singleton
server_properties& server_properties::getInstance() {
    static server_properties instance;
    return instance;
}

error server_properties::capture_if_needed() {
    error result = SUCCESS();
    if ( !captured_ ) {
        result = capture();
    }
    return result;
}

// Read server.config and fill server_properties::properties
error server_properties::capture() {
    error result = SUCCESS();
    std::string prop_name, prop_setting; // property name and setting

    FILE *fptr;
    char buf[BUF_LEN];
    char *fchar;
    int len;
    char *key;

    char DBKey[MAX_PASSWORD_LEN], DBPassword[MAX_PASSWORD_LEN];
    memset( &DBKey, '\0', MAX_PASSWORD_LEN );
    memset( &DBPassword, '\0', MAX_PASSWORD_LEN );

    std::string cfg_file;
    error ret = irods::get_full_path_for_config_file( SERVER_CONFIG_FILE, cfg_file );
    if ( !ret.ok() ) {
        return PASS( ret );
    }

    fptr = fopen( cfg_file.c_str(), "r" );

    if ( fptr == NULL ) {
        rodsLog( LOG_DEBUG,
                 "Cannot open SERVER_CONFIG_FILE file %s. errno = %d\n",
                 cfg_file.c_str(), errno );
        return ERROR( SYS_CONFIG_FILE_ERR, "server.config file error" );
    }

    buf[BUF_LEN - 1] = '\0';
    fchar = fgets( buf, BUF_LEN - 1, fptr );
    for ( ; fchar != '\0'; ) {
        if ( buf[0] == '#' || buf[0] == '/' ) {
            buf[0] = '\0'; /* Comment line, ignore */
        }

        /**
         * Parsing of server configuration settings
         */
        key = strstr( buf, DB_PASSWORD_KW );
        if ( key != NULL ) {
            len = strlen( DB_PASSWORD_KW );

            // Store password in temporary string
            snprintf( DBPassword, sizeof( DBPassword ), "%s", findNextTokenAndTerm( key + len ) );

        } // DB_PASSWORD_KW

        key = strstr( buf, DB_KEY_KW );
        if ( key != NULL ) {
            len = strlen( DB_KEY_KW );

            // Store key in temporary string
            snprintf( DBKey, MAX_PASSWORD_LEN, "%s", findNextTokenAndTerm( key + len ) );

        } // DB_KEY_KW

        // =-=-=-=-=-=-=-
        // PAM configuration - init PAM values
        result = properties.set<bool>( PAM_NO_EXTEND_KW, false );
        result = properties.set<size_t>( PAM_PW_LEN_KW, 20 );

        key = strstr( buf, DB_USERNAME_KW );
        if ( key != NULL ) {
            len = strlen( DB_USERNAME_KW );

            // Set property name and setting
            prop_name.assign( DB_USERNAME_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG1, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // DB_USERNAME_KW

        // =-=-=-=-=-=-=-
        // PAM configuration - init PAM values
        result = properties.set<bool>( PAM_NO_EXTEND_KW, false );
        result = properties.set<size_t>( PAM_PW_LEN_KW, 20 );

        prop_setting.assign( "121" );
        result = properties.set<std::string>( PAM_PW_MIN_TIME_KW, prop_setting );

        prop_setting.assign( "1209600" );
        result = properties.set<std::string>( PAM_PW_MAX_TIME_KW, prop_setting );
        // init PAM values

        key = strstr( buf, PAM_PW_LEN_KW );
        if ( key != NULL ) {
            len = strlen( PAM_PW_LEN_KW );

            // Set property name and setting
            prop_name.assign( PAM_PW_LEN_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<size_t>( prop_name, atoi( prop_setting.c_str() ) );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // PAM_PW_LEN_KW

        key = strstr( buf, PAM_NO_EXTEND_KW );
        if ( key != NULL ) {
            len = strlen( PAM_NO_EXTEND_KW );

            // Set property name and setting
            prop_name.assign( PAM_NO_EXTEND_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            std::transform( prop_setting.begin(), prop_setting.end(), prop_setting.begin(), ::tolower );
            if ( prop_setting == "true" ) {
                result = properties.set<bool>( PAM_NO_EXTEND_KW, true );
            }
            else {
                result = properties.set<bool>( PAM_NO_EXTEND_KW, false );
            }

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // PAM_NO_EXTEND_KW

        key = strstr( buf, PAM_PW_MIN_TIME_KW );
        if ( key != NULL ) {
            len = strlen( PAM_PW_MIN_TIME_KW );

            // Set property name and setting
            prop_name.assign( PAM_PW_MIN_TIME_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // PAM_PW_MIN_TIME_KW

        key = strstr( buf, PAM_PW_MAX_TIME_KW );
        if ( key != NULL ) {
            len = strlen( PAM_PW_MAX_TIME_KW );

            // Set property name and setting
            prop_name.assign( PAM_PW_MAX_TIME_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // PAM_PW_MAX_TIME_KW

        key = strstr( buf, RUN_SERVER_AS_ROOT_KW );
        if ( key != NULL ) {
            len = strlen( RUN_SERVER_AS_ROOT_KW );

            // Set property name and setting
            prop_name.assign( RUN_SERVER_AS_ROOT_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            std::transform( prop_setting.begin(), prop_setting.end(), prop_setting.begin(), ::tolower );
            if ( prop_setting == "true" ) {
                result = properties.set<bool>( RUN_SERVER_AS_ROOT_KW, true );
            }
            else {
                result = properties.set<bool>( RUN_SERVER_AS_ROOT_KW, false );
            }

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // RUN_SERVER_AS_ROOT_KW

        key = strstr( buf, DEF_DIR_MODE_KW );
        if ( key != NULL ) {
            len = strlen( DEF_DIR_MODE_KW );

            // Set property name and setting
            prop_name.assign( DEF_DIR_MODE_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<int>( prop_name, strtol( prop_setting.c_str(), 0, 0 ) );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // DEF_DIR_MODE_KW

        key = strstr( buf, DEF_FILE_MODE_KW );
        if ( key != NULL ) {
            len = strlen( DEF_FILE_MODE_KW );

            // Set property name and setting
            prop_name.assign( DEF_FILE_MODE_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<int>( prop_name, strtol( prop_setting.c_str(), 0, 0 ) );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // DEF_FILE_MODE_KW

        key = strstr( buf, CATALOG_DATABASE_TYPE_KW );
        if ( key != NULL ) {
            len = strlen( CATALOG_DATABASE_TYPE_KW );

            // Set property name and setting
            prop_name.assign( CATALOG_DATABASE_TYPE_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // CATALOG_DATABASE_TYPE_KW

        key = strstr( buf, KERBEROS_NAME_KW );
        if ( key != NULL ) {
            len = strlen( KERBEROS_NAME_KW );
            // Set property name and setting
            prop_name.assign( KERBEROS_NAME_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );
            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );
            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // KERBEROS_NAME_KW

        key = strstr( buf, KERBEROS_KEYTAB_KW );
        if ( key != NULL ) {
            len = strlen( KERBEROS_KEYTAB_KW );
            // Set property name and setting
            prop_name.assign( KERBEROS_KEYTAB_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );
            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );
            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );

            // Now set the appropriate kerberos environment variable
            setenv( "KRB5_KTNAME", prop_setting.c_str(), 1 );

        } // KERBEROS_KEYTAB_KW

        key = strstr( buf, DEFAULT_HASH_SCHEME_KW );
        if ( key != NULL ) {
            len = strlen( DEFAULT_HASH_SCHEME_KW );

            // Set property name and setting
            prop_name.assign( DEFAULT_HASH_SCHEME_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );
            std::transform(
                prop_setting.begin(),
                prop_setting.end(),
                prop_setting.begin(),
                ::tolower );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // DEFAULT_HASH_SCHEME_KW

        key = strstr( buf, MATCH_HASH_POLICY_KW );
        if ( key != NULL ) {
            len = strlen( MATCH_HASH_POLICY_KW );

            // Set property name and setting
            prop_name.assign( MATCH_HASH_POLICY_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );
            std::transform(
                prop_setting.begin(),
                prop_setting.end(),
                prop_setting.begin(),
                ::tolower );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // MATCH_HASH_POLICY_KW

        key = strstr( buf, LOCAL_ZONE_SID_KW );
        if ( key != NULL ) {
            len = strlen( LOCAL_ZONE_SID_KW );

            // Set property name and setting
            prop_name.assign( LOCAL_ZONE_SID_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );
        } // LOCAL_ZONE_SID_KW

        key = strstr( buf, REMOTE_ZONE_SID_KW );
        if ( key != NULL ) {
            result = SUCCESS();
            len = strlen( REMOTE_ZONE_SID_KW );

            // Set property name and setting
            prop_name.assign( REMOTE_ZONE_SID_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );
            rodsLog( LOG_DEBUG, "%s=%s", prop_name.c_str(), prop_setting.c_str() );

            // Update properties table
            std::vector<std::string> rem_sids;
            if( properties.has_entry( prop_name ) ) {
                result = properties.get< std::vector< std::string > >( prop_name, rem_sids );
                if( result.ok() ) {
                    rem_sids.push_back( prop_setting );
                }
            }

            if( result.ok() ) {
                rem_sids.push_back( prop_setting );
                result = properties.set< std::vector< std::string > >( prop_name, rem_sids );
            } else {
                irods::log( PASS( result ) );
            }

        } // REMOTE_ZONE_SID_KW

        key = strstr( buf, AGENT_KEY_KW.c_str() );
        if ( key != NULL ) {
            len = strlen( AGENT_KEY_KW.c_str() );
            // Set property name and setting
            prop_name.assign( AGENT_KEY_KW );
            prop_setting.assign( findNextTokenAndTerm( key + len ) );

            if ( 32 != prop_setting.size() )
            {
                rodsLog( LOG_ERROR,
                         "%s field in server.config must be 32 characters in length (currently %d characters in length).",
                         prop_name.c_str(), prop_setting.size() );
                fclose( fptr );
                return ERROR( SYS_CONFIG_FILE_ERR, "server.config file error" );
            }

            // Update properties table
            result = properties.set<std::string>( prop_name, prop_setting );

        } // AGENT_KEY_KW

        fchar = fgets( buf, BUF_LEN - 1, fptr );

    } // for ( ; fchar != '\0'; )

    fclose( fptr );

    // unscramble password
    if ( strlen( DBKey ) > 0 && strlen( DBPassword ) > 0 ) {
        char sPassword[MAX_PASSWORD_LEN + 10];
        strncpy( sPassword, DBPassword, MAX_PASSWORD_LEN );
        obfDecodeByKey( sPassword, DBKey, DBPassword );
        memset( sPassword, 0, MAX_PASSWORD_LEN );
    }

    // store password and key in server properties
    prop_name.assign( DB_PASSWORD_KW );
    prop_setting.assign( DBPassword );
    result = properties.set<std::string>( prop_name, prop_setting );
    rodsLog( LOG_DEBUG1, "%s=%s", prop_name.c_str(), prop_setting.c_str() );

    prop_name.assign( DB_KEY_KW );
    prop_setting.assign( DBKey );
    result = properties.set<std::string>( prop_name, prop_setting );
    rodsLog( LOG_DEBUG1, "%s=%s", prop_name.c_str(), prop_setting.c_str() );

    // set the captured flag so we no its already been captured
    captured_ = true;

    return result;

} // server_properties::capture()

} // namespace irods

<|endoftext|>


<commit_before>/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 Module:       FGFilter.cpp
 Author:       Jon S. Berndt
 Date started: 11/2000

 ------------- Copyright (C) 2000 -------------

 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.

 This program 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 General Public License for more
 details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 Place - Suite 330, Boston, MA  02111-1307, USA.

 Further information about the GNU General Public License can also be found on
 the world wide web at http://www.gnu.org.

FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------

HISTORY
--------------------------------------------------------------------------------

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES,  and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#include "FGFilter.h"

namespace JSBSim {

static const char *IdSrc = "$Id: FGFilter.cpp,v 1.39 2004/05/04 12:22:45 jberndt Exp $";
static const char *IdHdr = ID_FILTER;

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

FGFilter::FGFilter(FGFCS* fcs, FGConfigFile* AC_cfg) : FGFCSComponent(fcs),
                                                       AC_cfg(AC_cfg)
{
  string token;
  double denom;
  string sOutputIdx;

  Type = AC_cfg->GetValue("TYPE");
  Name = AC_cfg->GetValue("NAME");
  AC_cfg->GetNextConfigLine();
  dt = fcs->GetState()->Getdt();
  Trigger = 0;

  C1 = C2 = C3 = C4 = C5 = C6 = 0.0;

  if      (Type == "LAG_FILTER")          FilterType = eLag        ;
  else if (Type == "LEAD_LAG_FILTER")     FilterType = eLeadLag    ;
  else if (Type == "SECOND_ORDER_FILTER") FilterType = eOrder2     ;
  else if (Type == "WASHOUT_FILTER")      FilterType = eWashout    ;
  else if (Type == "INTEGRATOR")          FilterType = eIntegrator ;
  else                                    FilterType = eUnknown    ;

  while ((token = AC_cfg->GetValue()) != string("/COMPONENT")) {
    *AC_cfg >> token;
    if (token == "C1")           *AC_cfg >> C1;
    else if (token == "C2")      *AC_cfg >> C2;
    else if (token == "C3")      *AC_cfg >> C3;
    else if (token == "C4")      *AC_cfg >> C4;
    else if (token == "C5")      *AC_cfg >> C5;
    else if (token == "C6")      *AC_cfg >> C6;
    else if (token == "TRIGGER")
    {
      token = AC_cfg->GetValue("TRIGGER");
      *AC_cfg >> token;
      Trigger =  resolveSymbol(token);
    }
    else if (token == "INPUT")
    {
      token = AC_cfg->GetValue("INPUT");
      if( InputNodes.size() > 0 ) {
        cerr << "Filters can only accept one input" << endl;
      } else  {
        *AC_cfg >> token;
        InputNodes.push_back( resolveSymbol(token) );
      }
    }
    else if (token == "OUTPUT")
    {
      IsOutput = true;
      *AC_cfg >> sOutputIdx;
      OutputNode = PropertyManager->GetNode( sOutputIdx );
    }
    else cerr << "Unknown filter type: " << token << endl;
  }

  Initialize = true;

  switch (FilterType) {
    case eLag:
      denom = 2.00 + dt*C1;
      ca = dt*C1 / denom;
      cb = (2.00 - dt*C1) / denom;
      break;
    case eLeadLag:
      denom = 2.00*C3 + dt*C4;
      ca = (2.00*C1 + dt*C2) / denom;
      cb = (dt*C2 - 2.00*C1) / denom;
      cc = (2.00*C3 - dt*C4) / denom;
      break;
    case eOrder2:
      denom = 4.0*C4 + 2.0*C5*dt + C6*dt*dt;
      ca = (4.0*C1 + 2.0*C2*dt + C3*dt*dt) / denom;
      cb = (2.0*C3*dt*dt - 8.0*C1) / denom;
      cc = (4.0*C1 - 2.0*C2*dt + C3*dt*dt) / denom;
      cd = (2.0*C6*dt*dt - 8.0*C4) / denom;
      ce = (4.0*C4 - 2.0*C5*dt + C6*dt*dt) / denom;
      break;
    case eWashout:
      denom = 2.00 + dt*C1;
      ca = 2.00 / denom;
      cb = (2.00 - dt*C1) / denom;
      break;
    case eIntegrator:
      ca = dt*C1 / 2.00;
      break;
    case eUnknown:
      cerr << "Unknown filter type" << endl;
    break;
  }
  FGFCSComponent::bind();

  Debug(0);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

FGFilter::~FGFilter()
{
  Debug(1);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool FGFilter::Run(void)
{
  int test = 0;

  FGFCSComponent::Run(); // call the base class for initialization of Input

  if (Initialize) {

    PreviousOutput1 = PreviousInput1 = Output = Input;
    Initialize = false;

  } else if (Trigger != 0) {
    test = Trigger->getIntValue();
    if (test < 0) {
      Output = PreviousOutput1 = PreviousOutput2 = 0.0;
      Input  = PreviousInput1 = PreviousInput2 = 0.0;
    } else {
      Output = PreviousOutput1 = PreviousOutput2 = 0.0;
    }

  } else {
    Input = InputNodes[0]->getDoubleValue();
    switch (FilterType) {
      case eLag:
        Output = Input * ca + PreviousInput1 * ca + PreviousOutput1 * cb;
        break;
      case eLeadLag:
        Output = Input * ca + PreviousInput1 * cb + PreviousOutput1 * cc;
        break;
      case eOrder2:
        Output = Input * ca + PreviousInput1 * cb + PreviousInput2 * cc
                            - PreviousOutput1 * cd - PreviousOutput2 * ce;
        break;
      case eWashout:
        Output = Input * ca - PreviousInput1 * ca + PreviousOutput1 * cb;
        break;
      case eIntegrator:
        Output = Input * ca + PreviousInput1 * ca + PreviousOutput1;
        break;
      case eUnknown:
        break;
    }

  }

  PreviousOutput2 = PreviousOutput1;
  PreviousOutput1 = Output;
  PreviousInput2  = PreviousInput1;
  PreviousInput1  = Input;

  if (IsOutput) SetOutput();

  return true;
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//    The bitmasked value choices are as follows:
//    unset: In this case (the default) JSBSim would only print
//       out the normally expected messages, essentially echoing
//       the config files as they are read. If the environment
//       variable is not set, debug_lvl is set to 1 internally
//    0: This requests JSBSim not to output any messages
//       whatsoever.
//    1: This value explicity requests the normal JSBSim
//       startup messages
//    2: This value asks for a message to be printed out when
//       a class is instantiated
//    4: When this value is set, a message is displayed when a
//       FGModel object executes its Run() method
//    8: When this value is set, various runtime state variables
//       are printed out periodically
//    16: When set various parameters are sanity checked and
//       a message is printed out when they go out of bounds

void FGFilter::Debug(int from)
{
  if (debug_lvl <= 0) return;

  if (debug_lvl & 1) { // Standard console startup message output
    if (from == 0) { // Constructor
      cout << "      INPUT: " << InputNodes[0]->getName() << endl;
      cout << "      C1: " << C1 << endl;
      cout << "      C2: " << C2 << endl;
      cout << "      C3: " << C3 << endl;
      cout << "      C4: " << C4 << endl;
      cout << "      C5: " << C5 << endl;
      cout << "      C6: " << C6 << endl;
      if (IsOutput) cout << "      OUTPUT: " << OutputNode->getName() << endl;
    }
  }
  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
    if (from == 0) cout << "Instantiated: FGFilter" << endl;
    if (from == 1) cout << "Destroyed:    FGFilter" << endl;
  }
  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
  }
  if (debug_lvl & 8 ) { // Runtime state variables
  }
  if (debug_lvl & 16) { // Sanity checking
  }
  if (debug_lvl & 64) {
    if (from == 0) { // Constructor
      cout << IdSrc << endl;
      cout << IdHdr << endl;
    }
  }
}
}
<commit_msg>Fixed integrator wind-up problem<commit_after>/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 Module:       FGFilter.cpp
 Author:       Jon S. Berndt
 Date started: 11/2000

 ------------- Copyright (C) 2000 -------------

 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.

 This program 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 General Public License for more
 details.

 You should have received a copy of the GNU General Public License along with
 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 Place - Suite 330, Boston, MA  02111-1307, USA.

 Further information about the GNU General Public License can also be found on
 the world wide web at http://www.gnu.org.

FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------

HISTORY
--------------------------------------------------------------------------------

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES,  and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#include "FGFilter.h"

namespace JSBSim {

static const char *IdSrc = "$Id: FGFilter.cpp,v 1.40 2004/06/18 12:05:47 jberndt Exp $";
static const char *IdHdr = ID_FILTER;

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

FGFilter::FGFilter(FGFCS* fcs, FGConfigFile* AC_cfg) : FGFCSComponent(fcs),
                                                       AC_cfg(AC_cfg)
{
  string token;
  double denom;
  string sOutputIdx;

  Type = AC_cfg->GetValue("TYPE");
  Name = AC_cfg->GetValue("NAME");
  AC_cfg->GetNextConfigLine();
  dt = fcs->GetState()->Getdt();
  Trigger = 0;

  C1 = C2 = C3 = C4 = C5 = C6 = 0.0;

  if      (Type == "LAG_FILTER")          FilterType = eLag        ;
  else if (Type == "LEAD_LAG_FILTER")     FilterType = eLeadLag    ;
  else if (Type == "SECOND_ORDER_FILTER") FilterType = eOrder2     ;
  else if (Type == "WASHOUT_FILTER")      FilterType = eWashout    ;
  else if (Type == "INTEGRATOR")          FilterType = eIntegrator ;
  else                                    FilterType = eUnknown    ;

  while ((token = AC_cfg->GetValue()) != string("/COMPONENT")) {
    *AC_cfg >> token;
    if (token == "C1")           *AC_cfg >> C1;
    else if (token == "C2")      *AC_cfg >> C2;
    else if (token == "C3")      *AC_cfg >> C3;
    else if (token == "C4")      *AC_cfg >> C4;
    else if (token == "C5")      *AC_cfg >> C5;
    else if (token == "C6")      *AC_cfg >> C6;
    else if (token == "TRIGGER")
    {
      token = AC_cfg->GetValue("TRIGGER");
      *AC_cfg >> token;
      Trigger =  resolveSymbol(token);
    }
    else if (token == "INPUT")
    {
      token = AC_cfg->GetValue("INPUT");
      if( InputNodes.size() > 0 ) {
        cerr << "Filters can only accept one input" << endl;
      } else  {
        *AC_cfg >> token;
        InputNodes.push_back( resolveSymbol(token) );
      }
    }
    else if (token == "OUTPUT")
    {
      IsOutput = true;
      *AC_cfg >> sOutputIdx;
      OutputNode = PropertyManager->GetNode( sOutputIdx );
    }
    else cerr << "Unknown filter type: " << token << endl;
  }

  Initialize = true;

  switch (FilterType) {
    case eLag:
      denom = 2.00 + dt*C1;
      ca = dt*C1 / denom;
      cb = (2.00 - dt*C1) / denom;
      break;
    case eLeadLag:
      denom = 2.00*C3 + dt*C4;
      ca = (2.00*C1 + dt*C2) / denom;
      cb = (dt*C2 - 2.00*C1) / denom;
      cc = (2.00*C3 - dt*C4) / denom;
      break;
    case eOrder2:
      denom = 4.0*C4 + 2.0*C5*dt + C6*dt*dt;
      ca = (4.0*C1 + 2.0*C2*dt + C3*dt*dt) / denom;
      cb = (2.0*C3*dt*dt - 8.0*C1) / denom;
      cc = (4.0*C1 - 2.0*C2*dt + C3*dt*dt) / denom;
      cd = (2.0*C6*dt*dt - 8.0*C4) / denom;
      ce = (4.0*C4 - 2.0*C5*dt + C6*dt*dt) / denom;
      break;
    case eWashout:
      denom = 2.00 + dt*C1;
      ca = 2.00 / denom;
      cb = (2.00 - dt*C1) / denom;
      break;
    case eIntegrator:
      ca = dt*C1 / 2.00;
      break;
    case eUnknown:
      cerr << "Unknown filter type" << endl;
    break;
  }
  FGFCSComponent::bind();

  Debug(0);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

FGFilter::~FGFilter()
{
  Debug(1);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool FGFilter::Run(void)
{
  int test = 0;

  FGFCSComponent::Run(); // call the base class for initialization of Input

  if (Initialize) {

    PreviousOutput1 = PreviousInput1 = Output = Input;
    Initialize = false;

  } else if (Trigger != 0) {
    test = Trigger->getIntValue();
    if (test < 0) {
      Input  = PreviousInput1 = PreviousInput2 = 0.0;
    } else {
      Output = PreviousOutput1 = PreviousOutput2 = 0.0;
    }

  } else {
    Input = InputNodes[0]->getDoubleValue();
    switch (FilterType) {
      case eLag:
        Output = Input * ca + PreviousInput1 * ca + PreviousOutput1 * cb;
        break;
      case eLeadLag:
        Output = Input * ca + PreviousInput1 * cb + PreviousOutput1 * cc;
        break;
      case eOrder2:
        Output = Input * ca + PreviousInput1 * cb + PreviousInput2 * cc
                            - PreviousOutput1 * cd - PreviousOutput2 * ce;
        break;
      case eWashout:
        Output = Input * ca - PreviousInput1 * ca + PreviousOutput1 * cb;
        break;
      case eIntegrator:
        Output = Input * ca + PreviousInput1 * ca + PreviousOutput1;
        break;
      case eUnknown:
        break;
    }

  }

  PreviousOutput2 = PreviousOutput1;
  PreviousOutput1 = Output;
  PreviousInput2  = PreviousInput1;
  PreviousInput1  = Input;

  if (IsOutput) SetOutput();

  return true;
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//    The bitmasked value choices are as follows:
//    unset: In this case (the default) JSBSim would only print
//       out the normally expected messages, essentially echoing
//       the config files as they are read. If the environment
//       variable is not set, debug_lvl is set to 1 internally
//    0: This requests JSBSim not to output any messages
//       whatsoever.
//    1: This value explicity requests the normal JSBSim
//       startup messages
//    2: This value asks for a message to be printed out when
//       a class is instantiated
//    4: When this value is set, a message is displayed when a
//       FGModel object executes its Run() method
//    8: When this value is set, various runtime state variables
//       are printed out periodically
//    16: When set various parameters are sanity checked and
//       a message is printed out when they go out of bounds

void FGFilter::Debug(int from)
{
  if (debug_lvl <= 0) return;

  if (debug_lvl & 1) { // Standard console startup message output
    if (from == 0) { // Constructor
      cout << "      INPUT: " << InputNodes[0]->getName() << endl;
      cout << "      C1: " << C1 << endl;
      cout << "      C2: " << C2 << endl;
      cout << "      C3: " << C3 << endl;
      cout << "      C4: " << C4 << endl;
      cout << "      C5: " << C5 << endl;
      cout << "      C6: " << C6 << endl;
      if (IsOutput) cout << "      OUTPUT: " << OutputNode->getName() << endl;
    }
  }
  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
    if (from == 0) cout << "Instantiated: FGFilter" << endl;
    if (from == 1) cout << "Destroyed:    FGFilter" << endl;
  }
  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
  }
  if (debug_lvl & 8 ) { // Runtime state variables
  }
  if (debug_lvl & 16) { // Sanity checking
  }
  if (debug_lvl & 64) {
    if (from == 0) { // Constructor
      cout << IdSrc << endl;
      cout << IdHdr << endl;
    }
  }
}
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// InterleavedAttributeData:
//   Performance test for draws using interleaved attribute data in vertex buffers.
//

#include <sstream>

#include "ANGLEPerfTest.h"
#include "util/shader_utils.h"

using namespace angle;

namespace
{

struct InterleavedAttributeDataParams final : public RenderTestParams
{
    InterleavedAttributeDataParams()
    {
        iterationsPerStep = 1;

        // Common default values
        majorVersion = 2;
        minorVersion = 0;
        windowWidth  = 512;
        windowHeight = 512;
        numSprites   = 3000;
    }

    // static parameters
    unsigned int numSprites;
};

std::ostream &operator<<(std::ostream &os, const InterleavedAttributeDataParams &params)
{
    os << params.suffix().substr(1);

    if (params.eglParameters.majorVersion != EGL_DONT_CARE)
    {
        os << "_" << params.eglParameters.majorVersion << "_" << params.eglParameters.minorVersion;
    }

    return os;
}

class InterleavedAttributeDataBenchmark
    : public ANGLERenderTest,
      public ::testing::WithParamInterface<InterleavedAttributeDataParams>
{
  public:
    InterleavedAttributeDataBenchmark();

    void initializeBenchmark() override;
    void destroyBenchmark() override;
    void drawBenchmark() override;

  private:
    GLuint mPointSpriteProgram;
    GLuint mPositionColorBuffer[2];

    // The buffers contain two floats and 3 unsigned bytes per point sprite
    // Has to be aligned for float access on arm
    const size_t mBytesPerSpriteUnaligned = 2 * sizeof(float) + 3;
    const size_t mBytesPerSprite =
        ((mBytesPerSpriteUnaligned + sizeof(float) - 1) / sizeof(float)) * sizeof(float);
};

InterleavedAttributeDataBenchmark::InterleavedAttributeDataBenchmark()
    : ANGLERenderTest("InterleavedAttributeData", GetParam()), mPointSpriteProgram(0)
{
    // Timing out on Intel. http://crbug.com/921004
    if (GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
    {
        abortTest();
    }
}

void InterleavedAttributeDataBenchmark::initializeBenchmark()
{
    const auto &params = GetParam();

    // Compile point sprite shaders
    constexpr char kVS[] =
        "attribute vec4 aPosition;"
        "attribute vec4 aColor;"
        "varying vec4 vColor;"
        "void main()"
        "{"
        "    gl_PointSize = 25.0;"
        "    gl_Position  = aPosition;"
        "    vColor = aColor;"
        "}";

    constexpr char kFS[] =
        "precision mediump float;"
        "varying vec4 vColor;"
        "void main()"
        "{"
        "    gl_FragColor = vColor;"
        "}";

    mPointSpriteProgram = CompileProgram(kVS, kFS);
    ASSERT_NE(0u, mPointSpriteProgram);

    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);

    for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++)
    {
        // Set up initial data for pointsprite positions and colors
        std::vector<uint8_t> positionColorData(mBytesPerSprite * params.numSprites);
        for (unsigned int j = 0; j < params.numSprites; j++)
        {
            float pointSpriteX =
                (static_cast<float>(rand() % getWindow()->getWidth()) / getWindow()->getWidth()) *
                    2.0f -
                1.0f;
            float pointSpriteY =
                (static_cast<float>(rand() % getWindow()->getHeight()) / getWindow()->getHeight()) *
                    2.0f -
                1.0f;
            GLubyte pointSpriteRed   = static_cast<GLubyte>(rand() % 255);
            GLubyte pointSpriteGreen = static_cast<GLubyte>(rand() % 255);
            GLubyte pointSpriteBlue  = static_cast<GLubyte>(rand() % 255);

            // Add position data for the pointsprite
            *reinterpret_cast<float *>(
                &(positionColorData[j * mBytesPerSprite + 0 * sizeof(float) + 0])) =
                pointSpriteX;  // X
            *reinterpret_cast<float *>(
                &(positionColorData[j * mBytesPerSprite + 1 * sizeof(float) + 0])) =
                pointSpriteY;  // Y

            // Add color data for the pointsprite
            positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 0] = pointSpriteRed;    // R
            positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 1] = pointSpriteGreen;  // G
            positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 2] = pointSpriteBlue;   // B
        }

        // Generate the GL buffer with the position/color data
        glGenBuffers(1, &mPositionColorBuffer[i]);
        glBindBuffer(GL_ARRAY_BUFFER, mPositionColorBuffer[i]);
        glBufferData(GL_ARRAY_BUFFER, params.numSprites * mBytesPerSprite, &(positionColorData[0]),
                     GL_STATIC_DRAW);
    }

    ASSERT_GL_NO_ERROR();
}

void InterleavedAttributeDataBenchmark::destroyBenchmark()
{
    glDeleteProgram(mPointSpriteProgram);

    for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++)
    {
        glDeleteBuffers(1, &mPositionColorBuffer[i]);
    }
}

void InterleavedAttributeDataBenchmark::drawBenchmark()
{
    glClear(GL_COLOR_BUFFER_BIT);

    for (size_t k = 0; k < 20; k++)
    {
        for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++)
        {
            // Firstly get the attribute locations for the program
            glUseProgram(mPointSpriteProgram);
            GLint positionLocation = glGetAttribLocation(mPointSpriteProgram, "aPosition");
            ASSERT_NE(positionLocation, -1);
            GLint colorLocation = glGetAttribLocation(mPointSpriteProgram, "aColor");
            ASSERT_NE(colorLocation, -1);

            // Bind the position data from one buffer
            glBindBuffer(GL_ARRAY_BUFFER, mPositionColorBuffer[i]);
            glEnableVertexAttribArray(positionLocation);
            glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE,
                                  static_cast<GLsizei>(mBytesPerSprite), 0);

            // But bind the color data from the other buffer.
            glBindBuffer(GL_ARRAY_BUFFER,
                         mPositionColorBuffer[(i + 1) % ArraySize(mPositionColorBuffer)]);
            glEnableVertexAttribArray(colorLocation);
            glVertexAttribPointer(colorLocation, 3, GL_UNSIGNED_BYTE, GL_TRUE,
                                  static_cast<GLsizei>(mBytesPerSprite),
                                  reinterpret_cast<void *>(2 * sizeof(float)));

            // Then draw the colored pointsprites
            glDrawArrays(GL_POINTS, 0, GetParam().numSprites);

            glDisableVertexAttribArray(positionLocation);
            glDisableVertexAttribArray(colorLocation);
        }
    }

    ASSERT_GL_NO_ERROR();
}

TEST_P(InterleavedAttributeDataBenchmark, Run)
{
    run();
}

InterleavedAttributeDataParams D3D11Params()
{
    InterleavedAttributeDataParams params;
    params.eglParameters = egl_platform::D3D11();
    return params;
}

InterleavedAttributeDataParams D3D11_9_3Params()
{
    InterleavedAttributeDataParams params;
    params.eglParameters = egl_platform::D3D11_FL9_3();
    return params;
}

InterleavedAttributeDataParams D3D9Params()
{
    InterleavedAttributeDataParams params;
    params.eglParameters = egl_platform::D3D9();
    return params;
}

InterleavedAttributeDataParams OpenGLOrGLESParams()
{
    InterleavedAttributeDataParams params;
    params.eglParameters = egl_platform::OPENGL_OR_GLES(false);
    return params;
}

InterleavedAttributeDataParams VulkanParams()
{
    InterleavedAttributeDataParams params;
    params.eglParameters = egl_platform::VULKAN();
    return params;
}

ANGLE_INSTANTIATE_TEST(InterleavedAttributeDataBenchmark,
                       D3D11Params(),
                       D3D11_9_3Params(),
                       D3D9Params(),
                       OpenGLOrGLESParams(),
                       VulkanParams());

}  // anonymous namespace
<commit_msg>Fix skip for InterleavedAttributeDataBenchmark on GL.<commit_after>//
// Copyright (c) 2014 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// InterleavedAttributeData:
//   Performance test for draws using interleaved attribute data in vertex buffers.
//

#include <sstream>

#include "ANGLEPerfTest.h"
#include "util/shader_utils.h"

using namespace angle;

namespace
{

struct InterleavedAttributeDataParams final : public RenderTestParams
{
    InterleavedAttributeDataParams()
    {
        iterationsPerStep = 1;

        // Common default values
        majorVersion = 2;
        minorVersion = 0;
        windowWidth  = 512;
        windowHeight = 512;
        numSprites   = 3000;
    }

    // static parameters
    unsigned int numSprites;
};

std::ostream &operator<<(std::ostream &os, const InterleavedAttributeDataParams &params)
{
    os << params.suffix().substr(1);

    if (params.eglParameters.majorVersion != EGL_DONT_CARE)
    {
        os << "_" << params.eglParameters.majorVersion << "_" << params.eglParameters.minorVersion;
    }

    return os;
}

class InterleavedAttributeDataBenchmark
    : public ANGLERenderTest,
      public ::testing::WithParamInterface<InterleavedAttributeDataParams>
{
  public:
    InterleavedAttributeDataBenchmark();

    void initializeBenchmark() override;
    void destroyBenchmark() override;
    void drawBenchmark() override;

  private:
    GLuint mPointSpriteProgram;
    GLuint mPositionColorBuffer[2];

    // The buffers contain two floats and 3 unsigned bytes per point sprite
    // Has to be aligned for float access on arm
    const size_t mBytesPerSpriteUnaligned = 2 * sizeof(float) + 3;
    const size_t mBytesPerSprite =
        ((mBytesPerSpriteUnaligned + sizeof(float) - 1) / sizeof(float)) * sizeof(float);
};

InterleavedAttributeDataBenchmark::InterleavedAttributeDataBenchmark()
    : ANGLERenderTest("InterleavedAttributeData", GetParam()), mPointSpriteProgram(0)
{
    // Timing out on Intel. http://crbug.com/921004
    if (GetParam().eglParameters.renderer == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
    {
        mSkipTest = true;
    }
}

void InterleavedAttributeDataBenchmark::initializeBenchmark()
{
    const auto &params = GetParam();

    // Compile point sprite shaders
    constexpr char kVS[] =
        "attribute vec4 aPosition;"
        "attribute vec4 aColor;"
        "varying vec4 vColor;"
        "void main()"
        "{"
        "    gl_PointSize = 25.0;"
        "    gl_Position  = aPosition;"
        "    vColor = aColor;"
        "}";

    constexpr char kFS[] =
        "precision mediump float;"
        "varying vec4 vColor;"
        "void main()"
        "{"
        "    gl_FragColor = vColor;"
        "}";

    mPointSpriteProgram = CompileProgram(kVS, kFS);
    ASSERT_NE(0u, mPointSpriteProgram);

    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);

    for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++)
    {
        // Set up initial data for pointsprite positions and colors
        std::vector<uint8_t> positionColorData(mBytesPerSprite * params.numSprites);
        for (unsigned int j = 0; j < params.numSprites; j++)
        {
            float pointSpriteX =
                (static_cast<float>(rand() % getWindow()->getWidth()) / getWindow()->getWidth()) *
                    2.0f -
                1.0f;
            float pointSpriteY =
                (static_cast<float>(rand() % getWindow()->getHeight()) / getWindow()->getHeight()) *
                    2.0f -
                1.0f;
            GLubyte pointSpriteRed   = static_cast<GLubyte>(rand() % 255);
            GLubyte pointSpriteGreen = static_cast<GLubyte>(rand() % 255);
            GLubyte pointSpriteBlue  = static_cast<GLubyte>(rand() % 255);

            // Add position data for the pointsprite
            *reinterpret_cast<float *>(
                &(positionColorData[j * mBytesPerSprite + 0 * sizeof(float) + 0])) =
                pointSpriteX;  // X
            *reinterpret_cast<float *>(
                &(positionColorData[j * mBytesPerSprite + 1 * sizeof(float) + 0])) =
                pointSpriteY;  // Y

            // Add color data for the pointsprite
            positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 0] = pointSpriteRed;    // R
            positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 1] = pointSpriteGreen;  // G
            positionColorData[j * mBytesPerSprite + 2 * sizeof(float) + 2] = pointSpriteBlue;   // B
        }

        // Generate the GL buffer with the position/color data
        glGenBuffers(1, &mPositionColorBuffer[i]);
        glBindBuffer(GL_ARRAY_BUFFER, mPositionColorBuffer[i]);
        glBufferData(GL_ARRAY_BUFFER, params.numSprites * mBytesPerSprite, &(positionColorData[0]),
                     GL_STATIC_DRAW);
    }

    ASSERT_GL_NO_ERROR();
}

void InterleavedAttributeDataBenchmark::destroyBenchmark()
{
    glDeleteProgram(mPointSpriteProgram);

    for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++)
    {
        glDeleteBuffers(1, &mPositionColorBuffer[i]);
    }
}

void InterleavedAttributeDataBenchmark::drawBenchmark()
{
    glClear(GL_COLOR_BUFFER_BIT);

    for (size_t k = 0; k < 20; k++)
    {
        for (size_t i = 0; i < ArraySize(mPositionColorBuffer); i++)
        {
            // Firstly get the attribute locations for the program
            glUseProgram(mPointSpriteProgram);
            GLint positionLocation = glGetAttribLocation(mPointSpriteProgram, "aPosition");
            ASSERT_NE(positionLocation, -1);
            GLint colorLocation = glGetAttribLocation(mPointSpriteProgram, "aColor");
            ASSERT_NE(colorLocation, -1);

            // Bind the position data from one buffer
            glBindBuffer(GL_ARRAY_BUFFER, mPositionColorBuffer[i]);
            glEnableVertexAttribArray(positionLocation);
            glVertexAttribPointer(positionLocation, 2, GL_FLOAT, GL_FALSE,
                                  static_cast<GLsizei>(mBytesPerSprite), 0);

            // But bind the color data from the other buffer.
            glBindBuffer(GL_ARRAY_BUFFER,
                         mPositionColorBuffer[(i + 1) % ArraySize(mPositionColorBuffer)]);
            glEnableVertexAttribArray(colorLocation);
            glVertexAttribPointer(colorLocation, 3, GL_UNSIGNED_BYTE, GL_TRUE,
                                  static_cast<GLsizei>(mBytesPerSprite),
                                  reinterpret_cast<void *>(2 * sizeof(float)));

            // Then draw the colored pointsprites
            glDrawArrays(GL_POINTS, 0, GetParam().numSprites);

            glDisableVertexAttribArray(positionLocation);
            glDisableVertexAttribArray(colorLocation);
        }
    }

    ASSERT_GL_NO_ERROR();
}

TEST_P(InterleavedAttributeDataBenchmark, Run)
{
    run();
}

InterleavedAttributeDataParams D3D11Params()
{
    InterleavedAttributeDataParams params;
    params.eglParameters = egl_platform::D3D11();
    return params;
}

InterleavedAttributeDataParams D3D11_9_3Params()
{
    InterleavedAttributeDataParams params;
    params.eglParameters = egl_platform::D3D11_FL9_3();
    return params;
}

InterleavedAttributeDataParams D3D9Params()
{
    InterleavedAttributeDataParams params;
    params.eglParameters = egl_platform::D3D9();
    return params;
}

InterleavedAttributeDataParams OpenGLOrGLESParams()
{
    InterleavedAttributeDataParams params;
    params.eglParameters = egl_platform::OPENGL_OR_GLES(false);
    return params;
}

InterleavedAttributeDataParams VulkanParams()
{
    InterleavedAttributeDataParams params;
    params.eglParameters = egl_platform::VULKAN();
    return params;
}

ANGLE_INSTANTIATE_TEST(InterleavedAttributeDataBenchmark,
                       D3D11Params(),
                       D3D11_9_3Params(),
                       D3D9Params(),
                       OpenGLOrGLESParams(),
                       VulkanParams());

}  // anonymous namespace
<|endoftext|>