<commit_before>/*
 * Copyright (C) 2016,2017 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "logger.h"

#include <ctime>         // for time_t
#include <functional>    // for ref
#include <iostream>      // for cerr
#include <regex>         // for regex_replace, regex
#include <stdarg.h>      // for va_list, va_end, va_start
#include <stdexcept>     // for out_of_range
#include <stdio.h>       // for fileno, vsnprintf, stderr
#include <system_error>  // for system_error
#include <unistd.h>      // for isatty

#include "datetime.h"    // for to_string
#include "exception.h"   // for traceback
#include "utils.h"       // for get_thread_name

#define BUFFER_SIZE (500 * 1024)
#define STACKED_INDENT "<indent>"

std::atomic<uint64_t> logger_info_hook;

const std::regex filter_re("\033\\[[;\\d]*m", std::regex::optimize);
std::mutex Log::stack_mtx;
std::unordered_map<std::thread::id, unsigned> Log::stack_levels;
int Log::log_level = DEFAULT_LOG_LEVEL;
std::vector<std::unique_ptr<Logger>> Log::handlers;

static constexpr const char * const priorities[] = {
	EMERG_COL "█" NO_COL,   // LOG_EMERG    0 = System is unusable
	ALERT_COL "▉" NO_COL,   // LOG_ALERT    1 = Action must be taken immediately
	CRIT_COL "▊" NO_COL,    // LOG_CRIT     2 = Critical conditions
	ERR_COL "▋" NO_COL,     // LOG_ERR      3 = Error conditions
	WARNING_COL "▌" NO_COL, // LOG_WARNING  4 = Warning conditions
	NOTICE_COL "▍" NO_COL,  // LOG_NOTICE   5 = Normal but significant condition
	INFO_COL "▎" NO_COL,    // LOG_INFO     6 = Informational
	DEBUG_COL "▏" NO_COL,   // LOG_DEBUG    7 = Debug-level messages
	NO_COL,                 // VERBOSE    > 7 = Verbose messages
};

static inline constexpr int
validated_priority(int priority)
{
	if (priority < 0) {
		priority = -priority;
	}
	if (priority > LOG_DEBUG + 1) {
		priority = LOG_DEBUG + 1;
	}
	return priority;
}

void
println(bool with_endl, const char *format, va_list argptr, const void* obj, bool info)
{
	std::string str(Log::str_format(false, LOG_DEBUG, "", "", 0, "", "", obj, format, argptr, info));
	Log::log(LOG_DEBUG, str, 0, info, with_endl);
}

LogWrapper
_log(bool cleanup, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, const void*, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, ...)
{
	va_list argptr;
	va_start(argptr, format);
	auto ret = _log(cleanup, stacked, wakeup, async, priority, std::string(), file, line, suffix, prefix, obj, format, argptr);
	va_end(argptr);
	return ret;
}

LogWrapper
_log(bool cleanup, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, va_list argptr)
{
	return Log::log(cleanup, stacked, wakeup, async, priority, exc, file, line, suffix, prefix, obj, format, argptr);
}

LogWrapper::LogWrapper(LogWrapper&& o)
	: log(std::move(o.log))
{
	o.log.reset();
}

LogWrapper&
LogWrapper::operator=(LogWrapper&& o)
{
	log = std::move(o.log);
	o.log.reset();
	return *this;
}

LogWrapper::LogWrapper(LogType log_)
	: log(log_) { }

LogWrapper::~LogWrapper()
{
	if (log) {
		log->cleanup();
	}
	log.reset();
}

bool
LogWrapper::unlog(int priority, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, va_list argptr)
{
	return log->unlog(priority, file, line, suffix, prefix, obj, format, argptr);
}

bool
LogWrapper::clear()
{
	return log->clear();
}

long double
LogWrapper::age()
{
	return log->age();
}

LogType
LogWrapper::release()
{
	auto ret = log;
	log.reset();
	return ret;
}

void
StreamLogger::log(int priority, const std::string& str, bool with_priority, bool with_endl)
{
	ofs << std::regex_replace((with_priority ? priorities[validated_priority(priority)] : "") + str, filter_re, "");
	if (with_endl) {
		ofs << std::endl;
	}
}

void
StderrLogger::log(int priority, const std::string& str, bool with_priority, bool with_endl)
{
	if (isatty(fileno(stderr))) {
		std::cerr << (with_priority ? priorities[validated_priority(priority)] : "") + str;
	} else {
		std::cerr << std::regex_replace((with_priority ? priorities[validated_priority(priority)] : "") + str, filter_re, "");
	}
	if (with_endl) {
		std::cerr << std::endl;
	}
}

SysLog::SysLog(const char *ident, int option, int facility)
{
	openlog(ident, option, facility);
}

SysLog::~SysLog()
{
	closelog();
}

void
SysLog::log(int priority, const std::string& str, bool with_priority, bool)
{
	syslog(priority, "%s", std::regex_replace((with_priority ? priorities[validated_priority(priority)] : "") + str, filter_re, "").c_str());
}

Log::Log(const std::string& str, bool clean_, bool stacked_, bool async_, int priority_, std::chrono::time_point<std::chrono::system_clock> created_at_)
	: ScheduledTask(created_at_),
	  stack_level(0),
	  stacked(stacked_),
	  clean(clean_),
	  str_start(str),
	  async(async_),
	  priority(priority_),
	  cleaned(false)
{

	if (stacked) {
		std::lock_guard<std::mutex> lk(stack_mtx);
		thread_id = std::this_thread::get_id();
		try {
			stack_level = ++stack_levels.at(thread_id);
		} catch (const std::out_of_range&) {
			stack_levels[thread_id] = 0;
		}
	}
}

Log::~Log()
{
	cleanup();
}

void
Log::cleanup()
{
	unsigned long long c = 0;
	cleared_at.compare_exchange_strong(c, clean ? time_point_to_ullong(std::chrono::system_clock::now()) : 0);

	if (!cleaned.exchange(true)) {
		if (stacked) {
			std::lock_guard<std::mutex> lk(stack_mtx);
			if (stack_levels.at(thread_id)-- == 0) {
				stack_levels.erase(thread_id);
			}
		}
	}
}

long double
Log::age()
{
	auto now = (cleared_at > created_at) ? time_point_from_ullong(cleared_at) : std::chrono::system_clock::now();
	return std::chrono::duration_cast<std::chrono::nanoseconds>(now - time_point_from_ullong(created_at)).count();
}

/*
 * https://isocpp.org/wiki/faq/ctors#static-init-order
 * Avoid the "static initialization order fiasco"
 */

Scheduler&
Log::scheduler()
{
	static Scheduler scheduler("LOG");
	return scheduler;
}

void
Log::finish(int wait)
{
	scheduler().finish(wait);
}

void
Log::join()
{
	scheduler().join();
}

void
Log::run()
{
	L_INFO_HOOK_LOG("Log::run", this, "Log::run()");

	auto msg = str_start;
	auto log_age = age();
	if (log_age > 2e8) {
		msg += " ~" + delta_string(log_age, true);
	}
	Log::log(priority, msg, stack_level * 2);
}

std::string
Log::str_format(bool stacked, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void* obj, const char *format, va_list argptr, bool info)
{
	char* buffer = new char[BUFFER_SIZE];
	vsnprintf(buffer, BUFFER_SIZE, format, argptr);
	std::string msg(buffer);
	std::string result;
	if (info && validated_priority(priority) <= LOG_DEBUG) {
		auto iso8601 = "[" + Datetime::to_string(std::chrono::system_clock::now()) + "]";
		auto tid = " (" + get_thread_name() + ")";
		result = iso8601 + tid;
#ifdef LOG_OBJ_ADDRESS
		if (obj) {
			snprintf(buffer, BUFFER_SIZE, " [%p]", obj);
			result += buffer;
		}
#else
		(void)obj;
#endif
		result += " ";

#ifdef LOG_LOCATION
		result += std::string(file) + ":" + std::to_string(line) + ": ";
#endif
	}

	if (stacked) {
		result += STACKED_INDENT;
	}
	result += prefix + msg + suffix;
	delete []buffer;
	if (priority < 0) {
		if (exc.empty()) {
			result += DARK_GREY + traceback(file, line) + NO_COL;
		} else {
			result += NO_COL + exc + NO_COL;
		}
	}
	return result;
}

LogWrapper
Log::log(bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, va_list argptr)
{
	if (priority > log_level) {
		return LogWrapper(std::make_shared<Log>("", clean, stacked, async, priority, std::chrono::system_clock::now()));
	}

	std::string str(str_format(stacked, priority, exc, file, line, suffix, prefix, obj, format, argptr, true)); // TODO: Slow!

	return print(str, clean, stacked, wakeup, async, priority);
}

LogWrapper
Log::log(bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, ...)
{
	va_list argptr;
	va_start(argptr, format);
	auto ret = log(clean, stacked, wakeup, async, priority, exc, file, line, suffix, prefix, obj, format, argptr);
	va_end(argptr);

	return ret;
}

bool
Log::unlog(int priority, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, va_list argptr)
{
	if (!clear()) {
		if (priority > log_level) {
			return false;
		}

		std::string str(str_format(stacked, priority, std::string(), file, line, suffix, prefix, obj, format, argptr, true));

		print(str, false, stacked, 0, async, priority, time_point_from_ullong(created_at));

		return true;
	}
	return false;
}

bool
Log::unlog(int priority, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, ...)
{
	va_list argptr;
	va_start(argptr, format);
	auto ret = unlog(priority, file, line, suffix, prefix, obj, format, argptr);
	va_end(argptr);

	return ret;
}

LogWrapper
Log::add(const std::string& str, bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, std::chrono::time_point<std::chrono::system_clock> created_at)
{
	auto l_ptr = std::make_shared<Log>(str, clean, stacked, async, priority, created_at);

	scheduler().add(l_ptr, wakeup);

	return LogWrapper(l_ptr);
}

void
Log::log(int priority, std::string str, int indent, bool with_priority, bool with_endl)
{
	static std::mutex log_mutex;
	std::lock_guard<std::mutex> lk(log_mutex);
	auto needle = str.find(STACKED_INDENT);
	if (needle != std::string::npos) {
		str.replace(needle, sizeof(STACKED_INDENT) - 1, std::string(indent, ' '));
	}
	for (auto& handler : handlers) {
		handler->log(priority, str, with_priority, with_endl);
	}
}

LogWrapper
Log::print(const std::string& str, bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, std::chrono::time_point<std::chrono::system_clock> created_at)
{
	if (priority > log_level) {
		return LogWrapper(std::make_shared<Log>(str, clean, stacked, async, priority, created_at));
	}

	if (async || wakeup > std::chrono::system_clock::now()) {
		return add(str, clean, stacked, wakeup, async, priority, created_at);
	} else {
		auto l_ptr = std::make_shared<Log>(str, clean, stacked, async, priority, created_at);
		log(priority, str, l_ptr->stack_level * 2);
		return LogWrapper(l_ptr);
	}
}
<commit_msg>Tweaks<commit_after>/*
 * Copyright (C) 2016,2017 deipi.com LLC and contributors. All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "logger.h"

#include <ctime>         // for time_t
#include <functional>    // for ref
#include <iostream>      // for cerr
#include <regex>         // for regex_replace, regex
#include <stdarg.h>      // for va_list, va_end, va_start
#include <stdexcept>     // for out_of_range
#include <stdio.h>       // for fileno, vsnprintf, stderr
#include <system_error>  // for system_error
#include <unistd.h>      // for isatty

#include "datetime.h"    // for to_string
#include "exception.h"   // for traceback
#include "utils.h"       // for get_thread_name

#define BUFFER_SIZE (500 * 1024)
#define STACKED_INDENT "<indent>"

std::atomic<uint64_t> logger_info_hook;

const std::regex filter_re("\033\\[[;\\d]*m", std::regex::optimize);
std::mutex Log::stack_mtx;
std::unordered_map<std::thread::id, unsigned> Log::stack_levels;
int Log::log_level = DEFAULT_LOG_LEVEL;
std::vector<std::unique_ptr<Logger>> Log::handlers;

static constexpr const char * const priorities[] = {
	EMERG_COL "█" NO_COL,   // LOG_EMERG    0 = System is unusable
	ALERT_COL "▉" NO_COL,   // LOG_ALERT    1 = Action must be taken immediately
	CRIT_COL "▊" NO_COL,    // LOG_CRIT     2 = Critical conditions
	ERR_COL "▋" NO_COL,     // LOG_ERR      3 = Error conditions
	WARNING_COL "▌" NO_COL, // LOG_WARNING  4 = Warning conditions
	NOTICE_COL "▍" NO_COL,  // LOG_NOTICE   5 = Normal but significant condition
	INFO_COL "▎" NO_COL,    // LOG_INFO     6 = Informational
	DEBUG_COL "▏" NO_COL,   // LOG_DEBUG    7 = Debug-level messages
	NO_COL,                 // VERBOSE    > 7 = Verbose messages
};

static inline constexpr int
validated_priority(int priority)
{
	if (priority < 0) {
		priority = -priority;
	}
	if (priority > LOG_DEBUG + 1) {
		priority = LOG_DEBUG + 1;
	}
	return priority;
}

void
println(bool with_endl, const char *format, va_list argptr, const void* obj, bool info)
{
	std::string str(Log::str_format(false, LOG_DEBUG, "", "", 0, "", "", obj, format, argptr, info));
	Log::log(LOG_DEBUG, str, 0, info, with_endl);
}

LogWrapper
_log(bool cleanup, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, const void*, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, ...)
{
	va_list argptr;
	va_start(argptr, format);
	auto ret = _log(cleanup, stacked, wakeup, async, priority, std::string(), file, line, suffix, prefix, obj, format, argptr);
	va_end(argptr);
	return ret;
}

LogWrapper
_log(bool cleanup, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, va_list argptr)
{
	return Log::log(cleanup, stacked, wakeup, async, priority, exc, file, line, suffix, prefix, obj, format, argptr);
}

LogWrapper::LogWrapper(LogWrapper&& o)
	: log(std::move(o.log))
{
	o.log.reset();
}

LogWrapper::LogWrapper(LogType log_)
	: log(log_) { }

LogWrapper::~LogWrapper()
{
	if (log) {
		log->cleanup();
	}
	log.reset();
}

LogWrapper&
LogWrapper::operator=(LogWrapper&& o)
{
	log = std::move(o.log);
	o.log.reset();
	return *this;
}

bool
LogWrapper::unlog(int priority, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, va_list argptr)
{
	return log->unlog(priority, file, line, suffix, prefix, obj, format, argptr);
}

bool
LogWrapper::clear()
{
	return log->clear();
}

long double
LogWrapper::age()
{
	return log->age();
}

LogType
LogWrapper::release()
{
	auto ret = log;
	log.reset();
	return ret;
}

void
StreamLogger::log(int priority, const std::string& str, bool with_priority, bool with_endl)
{
	ofs << std::regex_replace((with_priority ? priorities[validated_priority(priority)] : "") + str, filter_re, "");
	if (with_endl) {
		ofs << std::endl;
	}
}

void
StderrLogger::log(int priority, const std::string& str, bool with_priority, bool with_endl)
{
	if (isatty(fileno(stderr))) {
		std::cerr << (with_priority ? priorities[validated_priority(priority)] : "") + str;
	} else {
		std::cerr << std::regex_replace((with_priority ? priorities[validated_priority(priority)] : "") + str, filter_re, "");
	}
	if (with_endl) {
		std::cerr << std::endl;
	}
}

SysLog::SysLog(const char *ident, int option, int facility)
{
	openlog(ident, option, facility);
}

SysLog::~SysLog()
{
	closelog();
}

void
SysLog::log(int priority, const std::string& str, bool with_priority, bool)
{
	syslog(priority, "%s", std::regex_replace((with_priority ? priorities[validated_priority(priority)] : "") + str, filter_re, "").c_str());
}

Log::Log(const std::string& str, bool clean_, bool stacked_, bool async_, int priority_, std::chrono::time_point<std::chrono::system_clock> created_at_)
	: ScheduledTask(created_at_),
	  stack_level(0),
	  stacked(stacked_),
	  clean(clean_),
	  str_start(str),
	  async(async_),
	  priority(priority_),
	  cleaned(false)
{

	if (stacked) {
		std::lock_guard<std::mutex> lk(stack_mtx);
		thread_id = std::this_thread::get_id();
		try {
			stack_level = ++stack_levels.at(thread_id);
		} catch (const std::out_of_range&) {
			stack_levels[thread_id] = 0;
		}
	}
}

Log::~Log()
{
	cleanup();
}

void
Log::cleanup()
{
	unsigned long long c = 0;
	cleared_at.compare_exchange_strong(c, clean ? time_point_to_ullong(std::chrono::system_clock::now()) : 0);

	if (!cleaned.exchange(true)) {
		if (stacked) {
			std::lock_guard<std::mutex> lk(stack_mtx);
			if (stack_levels.at(thread_id)-- == 0) {
				stack_levels.erase(thread_id);
			}
		}
	}
}

long double
Log::age()
{
	auto now = (cleared_at > created_at) ? time_point_from_ullong(cleared_at) : std::chrono::system_clock::now();
	return std::chrono::duration_cast<std::chrono::nanoseconds>(now - time_point_from_ullong(created_at)).count();
}

/*
 * https://isocpp.org/wiki/faq/ctors#static-init-order
 * Avoid the "static initialization order fiasco"
 */

Scheduler&
Log::scheduler()
{
	static Scheduler scheduler("LOG");
	return scheduler;
}

void
Log::finish(int wait)
{
	scheduler().finish(wait);
}

void
Log::join()
{
	scheduler().join();
}

void
Log::run()
{
	L_INFO_HOOK_LOG("Log::run", this, "Log::run()");

	auto msg = str_start;
	auto log_age = age();
	if (log_age > 2e8) {
		msg += " ~" + delta_string(log_age, true);
	}
	Log::log(priority, msg, stack_level * 2);
}

std::string
Log::str_format(bool stacked, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void* obj, const char *format, va_list argptr, bool info)
{
	char* buffer = new char[BUFFER_SIZE];
	vsnprintf(buffer, BUFFER_SIZE, format, argptr);
	std::string msg(buffer);
	std::string result;
	if (info && validated_priority(priority) <= LOG_DEBUG) {
		auto iso8601 = "[" + Datetime::to_string(std::chrono::system_clock::now()) + "]";
		auto tid = " (" + get_thread_name() + ")";
		result = iso8601 + tid;
#ifdef LOG_OBJ_ADDRESS
		if (obj) {
			snprintf(buffer, BUFFER_SIZE, " [%p]", obj);
			result += buffer;
		}
#else
		(void)obj;
#endif
		result += " ";

#ifdef LOG_LOCATION
		result += std::string(file) + ":" + std::to_string(line) + ": ";
#endif
	}

	if (stacked) {
		result += STACKED_INDENT;
	}
	result += prefix + msg + suffix;
	delete []buffer;
	if (priority < 0) {
		if (exc.empty()) {
			result += DARK_GREY + traceback(file, line) + NO_COL;
		} else {
			result += NO_COL + exc + NO_COL;
		}
	}
	return result;
}

LogWrapper
Log::log(bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, va_list argptr)
{
	if (priority > log_level) {
		return LogWrapper(std::make_shared<Log>("", clean, stacked, async, priority, std::chrono::system_clock::now()));
	}

	std::string str(str_format(stacked, priority, exc, file, line, suffix, prefix, obj, format, argptr, true)); // TODO: Slow!

	return print(str, clean, stacked, wakeup, async, priority);
}

LogWrapper
Log::log(bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, const std::string& exc, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, ...)
{
	va_list argptr;
	va_start(argptr, format);
	auto ret = log(clean, stacked, wakeup, async, priority, exc, file, line, suffix, prefix, obj, format, argptr);
	va_end(argptr);

	return ret;
}

bool
Log::unlog(int priority, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, va_list argptr)
{
	if (!clear()) {
		if (priority > log_level) {
			return false;
		}

		std::string str(str_format(stacked, priority, std::string(), file, line, suffix, prefix, obj, format, argptr, true));

		print(str, false, stacked, 0, async, priority, time_point_from_ullong(created_at));

		return true;
	}
	return false;
}

bool
Log::unlog(int priority, const char *file, int line, const char *suffix, const char *prefix, const void *obj, const char *format, ...)
{
	va_list argptr;
	va_start(argptr, format);
	auto ret = unlog(priority, file, line, suffix, prefix, obj, format, argptr);
	va_end(argptr);

	return ret;
}

LogWrapper
Log::add(const std::string& str, bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, std::chrono::time_point<std::chrono::system_clock> created_at)
{
	auto l_ptr = std::make_shared<Log>(str, clean, stacked, async, priority, created_at);

	scheduler().add(l_ptr, wakeup);

	return LogWrapper(l_ptr);
}

void
Log::log(int priority, std::string str, int indent, bool with_priority, bool with_endl)
{
	static std::mutex log_mutex;
	std::lock_guard<std::mutex> lk(log_mutex);
	auto needle = str.find(STACKED_INDENT);
	if (needle != std::string::npos) {
		str.replace(needle, sizeof(STACKED_INDENT) - 1, std::string(indent, ' '));
	}
	for (auto& handler : handlers) {
		handler->log(priority, str, with_priority, with_endl);
	}
}

LogWrapper
Log::print(const std::string& str, bool clean, bool stacked, std::chrono::time_point<std::chrono::system_clock> wakeup, bool async, int priority, std::chrono::time_point<std::chrono::system_clock> created_at)
{
	if (priority > log_level) {
		return LogWrapper(std::make_shared<Log>(str, clean, stacked, async, priority, created_at));
	}

	if (async || wakeup > std::chrono::system_clock::now()) {
		return add(str, clean, stacked, wakeup, async, priority, created_at);
	} else {
		auto l_ptr = std::make_shared<Log>(str, clean, stacked, async, priority, created_at);
		log(priority, str, l_ptr->stack_level * 2);
		return LogWrapper(l_ptr);
	}
}
<|endoftext|>


<commit_before>#ifndef x0_property_h
#define x0_property_h

// C++ template based properties, as defined in: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1615.pdf

#include <map>

namespace x0 {

template<class T>
class value_property
{
private:
	T value_;

public:
	value_property() : value_() {}
	value_property(T const& v) : value_(v) {}

	T operator()() const
	{
		return value_;
	}

	T operator()(T const& v)
	{
		value_ = v;
		return value_;
	}

	T get() const
	{
		return value_;
	}

	T set(T const& v)
	{
		value_ = v;
		return value_;
	}

	operator T() const
	{
		return value_;
	}

	T operator=(T const& v)
	{
		value_ = v;
		return value_;
	}

	typedef T value_type;
};

/** read-only value property. */
template<class T>
class rvalue_read_property
{
private:
	const T& value_;

public:
	template<class U>
	explicit rvalue_read_property(U v) : value_(v) {}

	T const& operator()() const
	{
		return value_;
	}

	T const& get() const
	{
		return value_;
	}

	operator T const&() const
	{
		return value_;
	}

	typedef T const& value_type;
};

template<
	typename T,
	typename Object,
	T (Object::*real_get)() const
>
class read_property
{
private:
	Object *object_;

public:
	read_property(Object *obj) : object_(obj)
	{
	}

	void operator()(Object *obj)
	{
		object_ = obj;
	}

	T operator()() const
	{
		return (object_->*real_get)();
	}

	T get() const
	{
		return (object_->*real_get)();
	}

	operator T() const
	{
		return (object_->*real_get)();
	}

	typedef T value_type;
};

template<
	typename T,
	typename Object,
	T (Object::*real_set)(T const&)
>
class write_property
{
private:
	Object *object_;

public:
	write_property(Object *obj) : object_(obj)
	{
	}

	write_property(Object *obj, T const & v) : object_(obj)
	{
		(object_->real_set)(v);
	}

	void operator()(Object *obj)
	{
		object_ = obj;
	}

	T operator()(T const& value)
	{
		return (object_->*real_set)(value);
	}

	T set(T const& value)
	{
		return (object_->*real_set)(value);
	}

	T operator=(T const& value)
	{
		return (object_->*real_set)(value);
	}

	typedef T value_type;
};

template<
	typename T,
	typename Object,
	T (Object::*real_get)() const,
	T (Object::*real_set)(T const&)
>
class property
{
private:
	Object *object_;

public:
	property(Object *obj) : object_(obj)
	{
	}

	property(Object *obj, T const & v) : object_(obj)
	{
		(object_->*real_set)(v);
	}

	void operator()(Object *obj)
	{
		object_ = obj;
	}

	void operator()(Object *obj, T const& value)
	{
		object_ = obj;
		(object_->*real_set)(value);
	}

	T operator()() const
	{
		return (object_->*real_get)();
	}

	T operator()(T const& value)
	{
		return (object_->*real_set)(value);
	}

	T get() const
	{
		return (object_->*real_get)();
	}

	T set(T const& value)
	{
		return (object_->*real_set)(value);
	}

	operator T() const
	{
		return (object_->*real_get)();
	}

	T operator=(T const& value)
	{
		return (object_->*real_set)(value);
	}

	typedef T value_type;
};

template<
	typename Key,
	typename T,
	class Compare = std::less<Key>,
	class Allocator = std::allocator<std::pair<const Key, T> >
>
class indexed_property
{
private:
	std::map<Key, T> data_;

	typedef typename std::map<Key, T, Compare, Allocator>::iterator map_iterator;

public:
	/** retrieve value of given key. */
	T operator()(Key const& key)
	{
		std::pair<map_iterator, bool> result;
		result = data_.insert(std::make_pair(key, T()));
		return (*result.first).second;
	}

	/** sets value for given key. */
	T operator()(Key const& key, T const& v)
	{
		std::pair<map_iterator, bool> result;
		result = data_.insert(std::make_pair(key, v));
		return (*result.first).second;
	}

	/** retrieve value of given key. */
	T get(Key const& key)
	{
		std::pair<map_iterator, bool> result;
		result = data_.insert(std::make_pair(key, T()));
		return (*result.first).second;
	}

	/** sets value for given key. */
	T set(Key const& key, T const& v)
	{
		std::pair<map_iterator, bool> result;
		result = data_.insert(std::make_pair(key, v));
		return (*result.first).second;
	}

	bool has(Key const& key) const
	{
		return data_.find(key) != data_.end();
	}

	std::size_t size() const
	{
		return data_.size();
	}

	map_iterator begin() const
	{
		return data_.begin();
	}

	map_iterator end() const
	{
		return data_.end();
	}

	T& operator[](Key const& key)
	{
		return (*((data_.insert(std::make_pair(key, T()))).first)).second;
	}
};

template<class T> class __ro_prop {
private:
	T value_;

public:
	template<typename... Args> __ro_prop(Args... args) : value_(args...) {}
	T& operator()() const { return value_; }
	T& get() const { return value_; }
	T *operator->() const { return &value_; }
};

} // namespace x0

#endif
<commit_msg>utils: added a += operator to value_property (i should add more operators, maybe)<commit_after>#ifndef x0_property_h
#define x0_property_h

// C++ template based properties, as defined in: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1615.pdf

#include <map>

namespace x0 {

template<class T>
class value_property
{
private:
	T value_;

public:
	value_property() : value_() {}
	value_property(T const& v) : value_(v) {}

	T operator()() const
	{
		return value_;
	}

	T operator()(T const& v)
	{
		value_ = v;
		return value_;
	}

	T get() const
	{
		return value_;
	}

	T set(T const& v)
	{
		value_ = v;
		return value_;
	}

	operator T() const
	{
		return value_;
	}

	T operator=(T const& v)
	{
		value_ = v;
		return value_;
	}

	T operator+=(const T& v)
	{
		value_ += v;
		return value_;
	}

	typedef T value_type;
};

/** read-only value property. */
template<class T>
class rvalue_read_property
{
private:
	const T& value_;

public:
	template<class U>
	explicit rvalue_read_property(U v) : value_(v) {}

	T const& operator()() const
	{
		return value_;
	}

	T const& get() const
	{
		return value_;
	}

	operator T const&() const
	{
		return value_;
	}

	typedef T const& value_type;
};

template<
	typename T,
	typename Object,
	T (Object::*real_get)() const
>
class read_property
{
private:
	Object *object_;

public:
	read_property(Object *obj) : object_(obj)
	{
	}

	void operator()(Object *obj)
	{
		object_ = obj;
	}

	T operator()() const
	{
		return (object_->*real_get)();
	}

	T get() const
	{
		return (object_->*real_get)();
	}

	operator T() const
	{
		return (object_->*real_get)();
	}

	typedef T value_type;
};

template<
	typename T,
	typename Object,
	T (Object::*real_set)(T const&)
>
class write_property
{
private:
	Object *object_;

public:
	write_property(Object *obj) : object_(obj)
	{
	}

	write_property(Object *obj, T const & v) : object_(obj)
	{
		(object_->real_set)(v);
	}

	void operator()(Object *obj)
	{
		object_ = obj;
	}

	T operator()(T const& value)
	{
		return (object_->*real_set)(value);
	}

	T set(T const& value)
	{
		return (object_->*real_set)(value);
	}

	T operator=(T const& value)
	{
		return (object_->*real_set)(value);
	}

	typedef T value_type;
};

template<
	typename T,
	typename Object,
	T (Object::*real_get)() const,
	T (Object::*real_set)(T const&)
>
class property
{
private:
	Object *object_;

public:
	property(Object *obj) : object_(obj)
	{
	}

	property(Object *obj, T const & v) : object_(obj)
	{
		(object_->*real_set)(v);
	}

	void operator()(Object *obj)
	{
		object_ = obj;
	}

	void operator()(Object *obj, T const& value)
	{
		object_ = obj;
		(object_->*real_set)(value);
	}

	T operator()() const
	{
		return (object_->*real_get)();
	}

	T operator()(T const& value)
	{
		return (object_->*real_set)(value);
	}

	T get() const
	{
		return (object_->*real_get)();
	}

	T set(T const& value)
	{
		return (object_->*real_set)(value);
	}

	operator T() const
	{
		return (object_->*real_get)();
	}

	T operator=(T const& value)
	{
		return (object_->*real_set)(value);
	}

	typedef T value_type;
};

template<
	typename Key,
	typename T,
	class Compare = std::less<Key>,
	class Allocator = std::allocator<std::pair<const Key, T> >
>
class indexed_property
{
private:
	std::map<Key, T> data_;

	typedef typename std::map<Key, T, Compare, Allocator>::iterator map_iterator;

public:
	/** retrieve value of given key. */
	T operator()(Key const& key)
	{
		std::pair<map_iterator, bool> result;
		result = data_.insert(std::make_pair(key, T()));
		return (*result.first).second;
	}

	/** sets value for given key. */
	T operator()(Key const& key, T const& v)
	{
		std::pair<map_iterator, bool> result;
		result = data_.insert(std::make_pair(key, v));
		return (*result.first).second;
	}

	/** retrieve value of given key. */
	T get(Key const& key)
	{
		std::pair<map_iterator, bool> result;
		result = data_.insert(std::make_pair(key, T()));
		return (*result.first).second;
	}

	/** sets value for given key. */
	T set(Key const& key, T const& v)
	{
		std::pair<map_iterator, bool> result;
		result = data_.insert(std::make_pair(key, v));
		return (*result.first).second;
	}

	bool has(Key const& key) const
	{
		return data_.find(key) != data_.end();
	}

	std::size_t size() const
	{
		return data_.size();
	}

	map_iterator begin() const
	{
		return data_.begin();
	}

	map_iterator end() const
	{
		return data_.end();
	}

	T& operator[](Key const& key)
	{
		return (*((data_.insert(std::make_pair(key, T()))).first)).second;
	}
};

template<class T> class __ro_prop {
private:
	T value_;

public:
	template<typename... Args> __ro_prop(Args... args) : value_(args...) {}
	T& operator()() const { return value_; }
	T& get() const { return value_; }
	T *operator->() const { return &value_; }
};

} // namespace x0

#endif
<|endoftext|>


<commit_before>/* Luwra
 * Minimal-overhead Lua wrapper for C++
 *
 * Copyright (C) 2015, Ole Krüger <ole@vprsm.de>
 */

#ifndef LUWRA_USERTYPES_H_
#define LUWRA_USERTYPES_H_

#include "common.hpp"
#include "types.hpp"
#include "stack.hpp"
#include "functions.hpp"

#include <map>

LUWRA_NS_BEGIN

namespace internal {
	using UserTypeID = const void*;

	template <typename T>
	using StripUserType = std::remove_cv_t<T>;

	/**
	 * User type identifier
	 */
	template <typename T>
	UserTypeID user_type_id = (void*) INTPTR_MAX;

	// TODO: Figure out why qualifying `user_type_reg_name` with `static` or `const` causes trouble
	/**
	 * Registry name for a meta table which is associated with a user type
	 */
	template <typename T>
	std::string user_type_reg_name =
		"UD#" + std::to_string(uintptr_t(&user_type_id<StripUserType<T>>));

	/**
	 * Register a new meta table for a user type T.
	 */
	template <typename U> static inline
	void new_user_type_id(State* state) {
		using T = StripUserType<U>;

		// Use the address as user type identifier
		luaL_newmetatable(state, user_type_reg_name<T>.c_str());
		user_type_id<T> = lua_topointer(state, -1);
	}

	/**
	 * Get the identifier for a user type at the given index.
	 */
	static inline
	UserTypeID get_user_type_id(State* state, int index) {
		if (!lua_isuserdata(state, index))
			return nullptr;

		if (lua_getmetatable(state, index)) {
			UserTypeID type_id = lua_topointer(state, -1);
			lua_pop(state, 1);
			return type_id;
		} else {
			return nullptr;
		}
	}

	/**
	 * Check if the value at the given index if a user type T.
	 */
	template <typename U> static inline
	StripUserType<U>* check_user_type(State* state, int index) {
		using T = StripUserType<U>;

		if (get_user_type_id(state, index) == user_type_id<T>) {
			return static_cast<T*>(lua_touserdata(state, index));
		} else {
			std::string error_msg =
				"Expected user type " + std::to_string(uintptr_t(user_type_id<T>));
			luaL_argerror(state, index, error_msg.c_str());
			return nullptr;
		}
	}

	template <typename U> static inline
	void apply_user_type_meta_table(State* state) {
		luaL_getmetatable(state, user_type_reg_name<StripUserType<U>>.c_str());
		lua_setmetatable(state, -2);
	}

	/**
	 * Lua C function to construct a user type T with parameters A
	 */
	template <typename U, typename... A> static inline
	int construct_user_type(State* state) {
		return internal::Layout<int(A...)>::direct(
			state,
			1,
			&Value<StripUserType<U>&>::template push<A...>,
			state
		);
	}

	/**
	 * Lua C function to destruct a user type T
	 */
	template <typename U> static inline
	int destruct_user_type(State* state) {
		using T = StripUserType<U>;

		if (!lua_islightuserdata(state, 1))
			Value<T&>::read(state, 1).~T();

		return 0;
	}

	/**
	 * Create a string representation for user type T.
	 */
	template <typename U> static
	int stringify_user_type(State* state) {
		using T = StripUserType<U>;

		return Value<std::string>::push(
			state,
			internal::user_type_reg_name<T>
				+ "@"
				+ std::to_string(uintptr_t(Value<T*>::read(state, 1)))
		);
	}

	// Normal property wrapper
	template <typename U, typename R>
	struct PropertyWrapper {
		using T = StripUserType<U>;

		template <R T::* property_pointer> static inline
		int invoke(State* state) {
			if (lua_gettop(state) > 1) {
				// Setter
				Value<T*>::read(state, 1)->*property_pointer = Value<R>::read(state, 2);
				return 0;
			} else {
				// Getter
				return push(state, Value<T*>::read(state, 1)->*property_pointer);
			}
		}
	};

	// Wrapper for constant properties
	template <typename U, typename R>
	struct PropertyWrapper<U, const R> {
		using T = StripUserType<U>;

		template <const R T::* property_pointer> static inline
		int invoke(State* state) {
			return push(state, Value<T*>::read(state, 1)->*property_pointer);
		}
	};

	template <typename T, typename S>
	struct MethodWrapper {
		static_assert(
			sizeof(T) == -1,
			"Undefined template MethodWrapper"
		);
	};

	// 'const volatile'-qualified methods
	template <typename T, typename R, typename... A>
	struct MethodWrapper<const volatile T, R(A...)> {
		using MethodPointerType = R (T::*)(A...) const volatile;
		using FunctionSignature = R (const volatile T*, A...);

		template <MethodPointerType method_pointer> static inline
		R call(const volatile T* parent, A... args) {
			return (parent->*method_pointer)(std::forward<A>(args)...);
		}
	};

	// 'const'-qualified methods
	template <typename T, typename R, typename... A>
	struct MethodWrapper<const T, R(A...)> {
		using MethodPointerType = R (T::*)(A...) const;
		using FunctionSignature = R (const T*, A...);

		template <MethodPointerType method_pointer> static inline
		R call(const T* parent, A... args) {
			return (parent->*method_pointer)(std::forward<A>(args)...);
		}
	};

	// 'volatile'-qualified methods
	template <typename T, typename R, typename... A>
	struct MethodWrapper<volatile T, R(A...)> {
		using MethodPointerType = R (T::*)(A...) volatile;
		using FunctionSignature = R (volatile T*, A...);

		template <MethodPointerType method_pointer> static inline
		R call(volatile T* parent, A... args) {
			return (parent->*method_pointer)(std::forward<A>(args)...);
		}
	};

	// unqualified methods
	template <typename T, typename R, typename... A>
	struct MethodWrapper<T, R(A...)> {
		using MethodPointerType = R (T::*)(A...);
		using FunctionSignature = R (T*, A...);

		template <MethodPointerType method_pointer> static inline
		R call(T* parent, A... args) {
			return (parent->*method_pointer)(std::forward<A>(args)...);
		}
	};
}

/**
 * User type T.
 * Instances created using this specialization are allocated and constructed as full user data
 * types in Lua. The default garbage-collecting hook will destruct the user type, once it has
 * been marked.
 */
template <typename U>
struct Value<U&> {
	using T = internal::StripUserType<U>;

	static inline
	T& read(State* state, int n) {
		// T is unqualified, therefore conversion from T& to U& is allowed
		return *internal::check_user_type<T>(state, n);
	}

	template <typename... A> static inline
	int push(State* state, A&&... args) {
		void* mem = lua_newuserdata(state, sizeof(T));

		if (!mem) {
			luaL_error(state, "Failed to allocate user type");
			return -1;
		}

		// Construct
		new (mem) T(std::forward<A>(args)...);

		// Apply meta table for unqualified type T
		internal::apply_user_type_meta_table<T>(state);

		return 1;
	}
};

/**
 * User type T.
 * Instances created using this specialization are allocated as light user data in Lua.
 * The default garbage-collector does not destruct light user data types.
 */
template <typename U>
struct Value<U*> {
	using T = internal::StripUserType<U>;

	static inline
	U* read(State* state, int n) {
		// T is unqualified, therefore conversion from T* to U* is allowed
		return internal::check_user_type<T>(state, n);
	}

	static inline
	int push(State* state, T* instance) {
		if (instance == nullptr)
			return 0;

		// Push instance as light user data
		lua_pushlightuserdata(state, instance);

		// Apply meta table for unqualified type T
		internal::apply_user_type_meta_table<T>(state);

		return 1;
	}
};

/**
 * Constructor function for a type `T`. Variadic arguments must be used to specify which parameters
 * to use during construction.
 */
template <typename T, typename... A>
constexpr CFunction wrap_constructor =
	&internal::construct_user_type<internal::StripUserType<T>, A...>;

/**
 * Works similiar to `wrap_function`. Given a class or struct declaration as follows:
 *
 *   struct T {
 *     R my_method(A0, A1 ... An);
 *   };
 *
 * You might wrap this method easily:
 *
 *   CFunction wrapped_meth = wrap_method<T, R(A0, A1 ... An), &T::my_method>;
 *
 * In Lua, assuming `instance` is a userdata instance of type `T`, x0, x1 ... xn are instances
 * of A0, A1 ... An, and the method has been bound as `my_method`; it is possible to invoke the
 * method like so:
 *
 *   instance:my_method(x0, x1 ... xn)
 */
template <
	typename T,
	typename S,
	typename internal::MethodWrapper<T, S>::MethodPointerType method_pointer
>
constexpr CFunction wrap_method =
	wrap_function<
		typename internal::MethodWrapper<T, S>::FunctionSignature,
		internal::MethodWrapper<T, S>::template call<method_pointer>
	>;

/**
 * Property accessor method
 *
 *   struct T {
 *     R my_property;
 *   };
 *
 * The wrapped property accessor is also a function:
 *
 *   CFunction wrapped_property = wrap_field<T, R, &T::my_property>;
 */
template <
	typename T,
	typename R,
	R T::* property_pointer
>
constexpr CFunction wrap_field =
	&internal::PropertyWrapper<T, R>::template invoke<property_pointer>;

/**
 * Register the metatable for user type `T`. This function allows you to register methods
 * which are shared across all instances of this type. A garbage-collector hook is also inserted.
 * Meta-methods can be added and/or overwritten aswell.
 */
template <typename U> static inline
void register_user_type(
	State* state,
	const std::map<const char*, CFunction>& methods,
	const std::map<const char*, CFunction>& meta_methods = std::map<const char*, CFunction>()
) {
	using T = internal::StripUserType<U>;

	// Setup an appropriate meta table name
	internal::new_user_type_id<T>(state);

	// Register methods
	if (methods.size() > 0 && meta_methods.count("__index") == 0) {
		push(state, "__index");
		lua_newtable(state);

		for (auto& method: methods) {
			push(state, method.first);
			push(state, method.second);
			lua_rawset(state, -3);
		}

		lua_rawset(state, -3);
	}

	// Register garbage-collection hook
	if (meta_methods.count("__gc") == 0) {
		push(state, "__gc");
		push(state, &internal::destruct_user_type<T>);
		lua_rawset(state, -3);
	}

	// Register string representation function
	if (meta_methods.count("__tostring") == 0) {
		push(state, "__tostring");
		push(state, &internal::stringify_user_type<T>);
		lua_rawset(state, -3);
	}

	// Insert meta methods
	for (const auto& metamethod: meta_methods) {
		push(state, metamethod.first);
		push(state, metamethod.second);
		lua_rawset(state, -3);
	}

	// Pop meta table off the stack
	lua_pop(state, -1);
}

LUWRA_NS_END

#endif
<commit_msg>Add some documentation to the user types translation unit<commit_after>/* Luwra
 * Minimal-overhead Lua wrapper for C++
 *
 * Copyright (C) 2015, Ole Krüger <ole@vprsm.de>
 */

#ifndef LUWRA_USERTYPES_H_
#define LUWRA_USERTYPES_H_

#include "common.hpp"
#include "types.hpp"
#include "stack.hpp"
#include "functions.hpp"

#include <map>

LUWRA_NS_BEGIN

namespace internal {
	using UserTypeID = const void*;

	template <typename T>
	using StripUserType = std::remove_cv_t<T>;

	/**
	 * User type identifier
	 */
	template <typename T>
	UserTypeID user_type_id = (void*) INTPTR_MAX;

	// TODO: Figure out why qualifying `user_type_reg_name` with `static` or `const` causes trouble
	/**
	 * Registry name for a meta table which is associated with a user type
	 */
	template <typename T>
	std::string user_type_reg_name =
		"UD#" + std::to_string(uintptr_t(&user_type_id<StripUserType<T>>));

	/**
	 * Register a new meta table for a user type T.
	 */
	template <typename U> static inline
	void new_user_type_id(State* state) {
		using T = StripUserType<U>;

		// Use the address as user type identifier
		luaL_newmetatable(state, user_type_reg_name<T>.c_str());
		user_type_id<T> = lua_topointer(state, -1);
	}

	/**
	 * Get the identifier for a user type at the given index.
	 */
	static inline
	UserTypeID get_user_type_id(State* state, int index) {
		if (!lua_isuserdata(state, index))
			return nullptr;

		if (lua_getmetatable(state, index)) {
			UserTypeID type_id = lua_topointer(state, -1);
			lua_pop(state, 1);
			return type_id;
		} else {
			return nullptr;
		}
	}

	/**
	 * Check if the value at the given index if a user type T.
	 */
	template <typename U> static inline
	StripUserType<U>* check_user_type(State* state, int index) {
		using T = StripUserType<U>;

		if (get_user_type_id(state, index) == user_type_id<T>) {
			return static_cast<T*>(lua_touserdata(state, index));
		} else {
			std::string error_msg =
				"Expected user type " + std::to_string(uintptr_t(user_type_id<T>));
			luaL_argerror(state, index, error_msg.c_str());
			return nullptr;
		}
	}

	/**
	 * Apply U's meta table for the value at the top of the stack.
	 */
	template <typename U> static inline
	void apply_user_type_meta_table(State* state) {
		luaL_getmetatable(state, user_type_reg_name<StripUserType<U>>.c_str());
		lua_setmetatable(state, -2);
	}

	/**
	 * Lua C function to construct a user type T with parameters A
	 */
	template <typename U, typename... A> static inline
	int construct_user_type(State* state) {
		return internal::Layout<int(A...)>::direct(
			state,
			1,
			&Value<StripUserType<U>&>::template push<A...>,
			state
		);
	}

	/**
	 * Lua C function to destruct a user type T
	 */
	template <typename U> static inline
	int destruct_user_type(State* state) {
		using T = StripUserType<U>;

		if (!lua_islightuserdata(state, 1))
			Value<T&>::read(state, 1).~T();

		return 0;
	}

	/**
	 * Create a string representation for user type T.
	 */
	template <typename U> static
	int stringify_user_type(State* state) {
		using T = StripUserType<U>;

		return Value<std::string>::push(
			state,
			internal::user_type_reg_name<T>
				+ "@"
				+ std::to_string(uintptr_t(Value<T*>::read(state, 1)))
		);
	}

	/**
	 * Helper struct for wrapping user type fields
	 */
	template <typename U, typename R>
	struct FieldWrapper {
		using T = StripUserType<U>;

		template <R T::* field_pointer> static inline
		int invoke(State* state) {
			if (lua_gettop(state) > 1) {
				// Setter
				Value<T*>::read(state, 1)->*field_pointer = Value<R>::read(state, 2);
				return 0;
			} else {
				// Getter
				return push(state, Value<T*>::read(state, 1)->*field_pointer);
			}
		}
	};

	// 'const'-qualified fields
	template <typename U, typename R>
	struct FieldWrapper<U, const R> {
		using T = StripUserType<U>;

		template <const R T::* field_pointer> static inline
		int invoke(State* state) {
			return push(state, Value<T*>::read(state, 1)->*field_pointer);
		}
	};

	/**
	 * Helper struct for wrapping user type methods
	 */
	template <typename T, typename S>
	struct MethodWrapper {
		static_assert(
			sizeof(T) == -1,
			"Undefined template MethodWrapper"
		);
	};

	// 'const volatile'-qualified methods
	template <typename T, typename R, typename... A>
	struct MethodWrapper<const volatile T, R(A...)> {
		using MethodPointerType = R (T::*)(A...) const volatile;
		using FunctionSignature = R (const volatile T*, A...);

		template <MethodPointerType method_pointer> static inline
		R call(const volatile T* parent, A... args) {
			return (parent->*method_pointer)(std::forward<A>(args)...);
		}
	};

	// 'const'-qualified methods
	template <typename T, typename R, typename... A>
	struct MethodWrapper<const T, R(A...)> {
		using MethodPointerType = R (T::*)(A...) const;
		using FunctionSignature = R (const T*, A...);

		template <MethodPointerType method_pointer> static inline
		R call(const T* parent, A... args) {
			return (parent->*method_pointer)(std::forward<A>(args)...);
		}
	};

	// 'volatile'-qualified methods
	template <typename T, typename R, typename... A>
	struct MethodWrapper<volatile T, R(A...)> {
		using MethodPointerType = R (T::*)(A...) volatile;
		using FunctionSignature = R (volatile T*, A...);

		template <MethodPointerType method_pointer> static inline
		R call(volatile T* parent, A... args) {
			return (parent->*method_pointer)(std::forward<A>(args)...);
		}
	};

	// unqualified methods
	template <typename T, typename R, typename... A>
	struct MethodWrapper<T, R(A...)> {
		using MethodPointerType = R (T::*)(A...);
		using FunctionSignature = R (T*, A...);

		template <MethodPointerType method_pointer> static inline
		R call(T* parent, A... args) {
			return (parent->*method_pointer)(std::forward<A>(args)...);
		}
	};
}

/**
 * User type T.
 * Instances created using this specialization are allocated and constructed as full user data
 * types in Lua. The default garbage-collecting hook will destruct the user type, once it has
 * been marked.
 */
template <typename U>
struct Value<U&> {
	using T = internal::StripUserType<U>;

	static inline
	U& read(State* state, int n) {
		// T is unqualified, therefore conversion from T& to U& is allowed
		return *internal::check_user_type<T>(state, n);
	}

	template <typename... A> static inline
	int push(State* state, A&&... args) {
		void* mem = lua_newuserdata(state, sizeof(T));

		if (!mem) {
			luaL_error(state, "Failed to allocate user type");
			return -1;
		}

		// Construct
		new (mem) T(std::forward<A>(args)...);

		// Apply meta table for unqualified type T
		internal::apply_user_type_meta_table<T>(state);

		return 1;
	}
};

/**
 * User type T.
 * Instances created using this specialization are allocated as light user data in Lua.
 * The default garbage-collector does not destruct light user data types.
 */
template <typename U>
struct Value<U*> {
	using T = internal::StripUserType<U>;

	static inline
	U* read(State* state, int n) {
		// T is unqualified, therefore conversion from T* to U* is allowed
		return internal::check_user_type<T>(state, n);
	}

	static inline
	int push(State* state, T* instance) {
		if (instance == nullptr)
			return 0;

		// Push instance as light user data
		lua_pushlightuserdata(state, instance);

		// Apply meta table for unqualified type T
		internal::apply_user_type_meta_table<T>(state);

		return 1;
	}
};

/**
 * Constructor function for a type `T`. Variadic arguments must be used to specify which parameters
 * to use during construction.
 */
template <typename T, typename... A>
constexpr CFunction wrap_constructor =
	&internal::construct_user_type<internal::StripUserType<T>, A...>;

/**
 * Works similiar to `wrap_function`. Given a class or struct declaration as follows:
 *
 *   struct T {
 *     R my_method(A0, A1 ... An);
 *   };
 *
 * You might wrap this method easily:
 *
 *   CFunction wrapped_meth = wrap_method<T, R(A0, A1 ... An), &T::my_method>;
 *
 * In Lua, assuming `instance` is a userdata instance of type `T`, x0, x1 ... xn are instances
 * of A0, A1 ... An, and the method has been bound as `my_method`; it is possible to invoke the
 * method like so:
 *
 *   instance:my_method(x0, x1 ... xn)
 */
template <
	typename T,
	typename S,
	typename internal::MethodWrapper<T, S>::MethodPointerType method_pointer
>
constexpr CFunction wrap_method =
	wrap_function<
		typename internal::MethodWrapper<T, S>::FunctionSignature,
		internal::MethodWrapper<T, S>::template call<method_pointer>
	>;

/**
 * Property accessor method
 *
 *   struct T {
 *     R my_property;
 *   };
 *
 * The wrapped property accessor is also a function:
 *
 *   CFunction wrapped_property = wrap_field<T, R, &T::my_property>;
 */
template <
	typename T,
	typename R,
	R T::* field_pointer
>
constexpr CFunction wrap_field =
	&internal::FieldWrapper<T, R>::template invoke<field_pointer>;

/**
 * Register the metatable for user type `T`. This function allows you to register methods
 * which are shared across all instances of this type. A garbage-collector hook is also inserted.
 * Meta-methods can be added and/or overwritten aswell.
 */
template <typename U> static inline
void register_user_type(
	State* state,
	const std::map<const char*, CFunction>& methods,
	const std::map<const char*, CFunction>& meta_methods = std::map<const char*, CFunction>()
) {
	using T = internal::StripUserType<U>;

	// Setup an appropriate meta table name
	internal::new_user_type_id<T>(state);

	// Register methods
	if (methods.size() > 0 && meta_methods.count("__index") == 0) {
		push(state, "__index");
		lua_newtable(state);

		for (auto& method: methods) {
			push(state, method.first);
			push(state, method.second);
			lua_rawset(state, -3);
		}

		lua_rawset(state, -3);
	}

	// Register garbage-collection hook
	if (meta_methods.count("__gc") == 0) {
		push(state, "__gc");
		push(state, &internal::destruct_user_type<T>);
		lua_rawset(state, -3);
	}

	// Register string representation function
	if (meta_methods.count("__tostring") == 0) {
		push(state, "__tostring");
		push(state, &internal::stringify_user_type<T>);
		lua_rawset(state, -3);
	}

	// Insert meta methods
	for (const auto& metamethod: meta_methods) {
		push(state, metamethod.first);
		push(state, metamethod.second);
		lua_rawset(state, -3);
	}

	// Pop meta table off the stack
	lua_pop(state, -1);
}

LUWRA_NS_END

#endif
<|endoftext|>


<commit_before>#include "pldm_base_cmd.hpp"

#include "pldm_cmd_helper.hpp"

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "libpldm/utils.h"

namespace pldmtool
{

namespace base
{

namespace
{

using namespace pldmtool::helper;

std::vector<std::unique_ptr<CommandInterface>> commands;
const std::map<const char*, pldm_supported_types> pldmTypes{
    {"base", PLDM_BASE}, {"platform", PLDM_PLATFORM}, {"bios", PLDM_BIOS},
    {"fru", PLDM_FRU},   {"oem", PLDM_OEM},
};

} // namespace

class GetPLDMTypes : public CommandInterface
{
  public:
    ~GetPLDMTypes() = default;
    GetPLDMTypes() = delete;
    GetPLDMTypes(const GetPLDMTypes&) = delete;
    GetPLDMTypes(GetPLDMTypes&&) = default;
    GetPLDMTypes& operator=(const GetPLDMTypes&) = delete;
    GetPLDMTypes& operator=(GetPLDMTypes&&) = default;

    using CommandInterface::CommandInterface;

    std::pair<int, std::vector<uint8_t>> createRequestMsg() override
    {
        std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr));
        auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
        auto rc = encode_get_types_req(PLDM_LOCAL_INSTANCE_ID, request);
        return {rc, requestMsg};
    }

    void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
    {
        uint8_t cc = 0;
        std::vector<bitfield8_t> types(8);
        auto rc = decode_get_types_resp(responsePtr, payloadLength, &cc,
                                        types.data());
        if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
        {
            std::cerr << "Response Message Error: "
                      << "rc=" << rc << ",cc=" << (int)cc << "\n";
            return;
        }

        printPldmTypes(types);
    }

  private:
    void printPldmTypes(std::vector<bitfield8_t>& types)
    {
        std::cout << "Supported types:";
        for (int i = 0; i < PLDM_MAX_TYPES; i++)
        {
            bitfield8_t b = types[i / 8];
            if (b.byte & (1 << i % 8))
            {
                std::cout << " " << i;
                auto it = std::find_if(
                    pldmTypes.begin(), pldmTypes.end(),
                    [i](const auto& typePair) { return typePair.second == i; });
                if (it != pldmTypes.end())
                {

                    std::cout << "(" << it->first << ")";
                }
            }
        }

        std::cout << std::endl;
    }
};

class GetPLDMVersion : public CommandInterface
{
  public:
    ~GetPLDMVersion() = default;
    GetPLDMVersion() = delete;
    GetPLDMVersion(const GetPLDMVersion&) = delete;
    GetPLDMVersion(GetPLDMVersion&&) = default;
    GetPLDMVersion& operator=(const GetPLDMVersion&) = delete;
    GetPLDMVersion& operator=(GetPLDMVersion&&) = default;

    explicit GetPLDMVersion(const char* type, const char* name, CLI::App* app) :
        CommandInterface(type, name, app)
    {
        app->add_option("-t,--type", pldmType, "pldm supported type")
            ->required()
            ->transform(CLI::CheckedTransformer(pldmTypes, CLI::ignore_case));
    }
    std::pair<int, std::vector<uint8_t>> createRequestMsg() override
    {
        std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
                                        PLDM_GET_VERSION_REQ_BYTES);
        auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());

        auto rc = encode_get_version_req(PLDM_LOCAL_INSTANCE_ID, 0,
                                         PLDM_GET_FIRSTPART, pldmType, request);
        return {rc, requestMsg};
    }

    void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
    {
        uint8_t cc = 0, transferFlag = 0;
        uint32_t transferHandle = 0;
        ver32_t version;
        auto rc =
            decode_get_version_resp(responsePtr, payloadLength, &cc,
                                    &transferHandle, &transferFlag, &version);
        if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
        {
            std::cerr << "Response Message Error: "
                      << "rc=" << rc << ",cc=" << (int)cc << "\n";
            return;
        }
        char buffer[16] = {0};
        ver2str(&version, buffer, sizeof(buffer));
        std::cout << "Type " << pldmType;
        auto it = std::find_if(
            pldmTypes.begin(), pldmTypes.end(),
            [&](const auto& typePair) { return typePair.second == pldmType; });

        if (it != pldmTypes.end())
        {
            std::cout << "(" << it->first << ")";
        }
        std::cout << ": " << buffer << std::endl;
    }

  private:
    pldm_supported_types pldmType;
};

void registerCommand(CLI::App& app)
{
    auto base = app.add_subcommand("base", "base type command");
    base->require_subcommand(1);
    auto getPLDMTypes =
        base->add_subcommand("GetPLDMTypes", "get pldm supported types");
    commands.push_back(
        std::make_unique<GetPLDMTypes>("base", "GetPLDMTypes", getPLDMTypes));

    auto getPLDMVersion =
        base->add_subcommand("GetPLDMVersion", "get version of a certain type");
    commands.push_back(std::make_unique<GetPLDMVersion>(
        "base", "GetPLDMVersion", getPLDMVersion));
}

} // namespace base
} // namespace pldmtool
<commit_msg>pldmtool: BASE: Implement GetTID command<commit_after>#include "pldm_base_cmd.hpp"

#include "pldm_cmd_helper.hpp"

#include <map>
#include <memory>
#include <string>
#include <vector>

#include "libpldm/utils.h"

namespace pldmtool
{

namespace base
{

namespace
{

using namespace pldmtool::helper;

std::vector<std::unique_ptr<CommandInterface>> commands;
const std::map<const char*, pldm_supported_types> pldmTypes{
    {"base", PLDM_BASE}, {"platform", PLDM_PLATFORM}, {"bios", PLDM_BIOS},
    {"fru", PLDM_FRU},   {"oem", PLDM_OEM},
};

} // namespace

class GetPLDMTypes : public CommandInterface
{
  public:
    ~GetPLDMTypes() = default;
    GetPLDMTypes() = delete;
    GetPLDMTypes(const GetPLDMTypes&) = delete;
    GetPLDMTypes(GetPLDMTypes&&) = default;
    GetPLDMTypes& operator=(const GetPLDMTypes&) = delete;
    GetPLDMTypes& operator=(GetPLDMTypes&&) = default;

    using CommandInterface::CommandInterface;

    std::pair<int, std::vector<uint8_t>> createRequestMsg() override
    {
        std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr));
        auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
        auto rc = encode_get_types_req(PLDM_LOCAL_INSTANCE_ID, request);
        return {rc, requestMsg};
    }

    void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
    {
        uint8_t cc = 0;
        std::vector<bitfield8_t> types(8);
        auto rc = decode_get_types_resp(responsePtr, payloadLength, &cc,
                                        types.data());
        if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
        {
            std::cerr << "Response Message Error: "
                      << "rc=" << rc << ",cc=" << (int)cc << "\n";
            return;
        }

        printPldmTypes(types);
    }

  private:
    void printPldmTypes(std::vector<bitfield8_t>& types)
    {
        std::cout << "Supported types:";
        for (int i = 0; i < PLDM_MAX_TYPES; i++)
        {
            bitfield8_t b = types[i / 8];
            if (b.byte & (1 << i % 8))
            {
                std::cout << " " << i;
                auto it = std::find_if(
                    pldmTypes.begin(), pldmTypes.end(),
                    [i](const auto& typePair) { return typePair.second == i; });
                if (it != pldmTypes.end())
                {

                    std::cout << "(" << it->first << ")";
                }
            }
        }

        std::cout << std::endl;
    }
};

class GetPLDMVersion : public CommandInterface
{
  public:
    ~GetPLDMVersion() = default;
    GetPLDMVersion() = delete;
    GetPLDMVersion(const GetPLDMVersion&) = delete;
    GetPLDMVersion(GetPLDMVersion&&) = default;
    GetPLDMVersion& operator=(const GetPLDMVersion&) = delete;
    GetPLDMVersion& operator=(GetPLDMVersion&&) = default;

    explicit GetPLDMVersion(const char* type, const char* name, CLI::App* app) :
        CommandInterface(type, name, app)
    {
        app->add_option("-t,--type", pldmType, "pldm supported type")
            ->required()
            ->transform(CLI::CheckedTransformer(pldmTypes, CLI::ignore_case));
    }
    std::pair<int, std::vector<uint8_t>> createRequestMsg() override
    {
        std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr) +
                                        PLDM_GET_VERSION_REQ_BYTES);
        auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());

        auto rc = encode_get_version_req(PLDM_LOCAL_INSTANCE_ID, 0,
                                         PLDM_GET_FIRSTPART, pldmType, request);
        return {rc, requestMsg};
    }

    void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
    {
        uint8_t cc = 0, transferFlag = 0;
        uint32_t transferHandle = 0;
        ver32_t version;
        auto rc =
            decode_get_version_resp(responsePtr, payloadLength, &cc,
                                    &transferHandle, &transferFlag, &version);
        if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
        {
            std::cerr << "Response Message Error: "
                      << "rc=" << rc << ",cc=" << (int)cc << "\n";
            return;
        }
        char buffer[16] = {0};
        ver2str(&version, buffer, sizeof(buffer));
        std::cout << "Type " << pldmType;
        auto it = std::find_if(
            pldmTypes.begin(), pldmTypes.end(),
            [&](const auto& typePair) { return typePair.second == pldmType; });

        if (it != pldmTypes.end())
        {
            std::cout << "(" << it->first << ")";
        }
        std::cout << ": " << buffer << std::endl;
    }

  private:
    pldm_supported_types pldmType;
};

class GetTID : public CommandInterface
{
  public:
    ~GetTID() = default;
    GetTID() = delete;
    GetTID(const GetTID&) = delete;
    GetTID(GetTID&&) = default;
    GetTID& operator=(const GetTID&) = delete;
    GetTID& operator=(GetTID&&) = default;

    using CommandInterface::CommandInterface;

    std::pair<int, std::vector<uint8_t>> createRequestMsg() override
    {
        std::vector<uint8_t> requestMsg(sizeof(pldm_msg_hdr));
        auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
        auto rc = encode_get_tid_req(PLDM_LOCAL_INSTANCE_ID, request);
        return {rc, requestMsg};
    }

    void parseResponseMsg(pldm_msg* responsePtr, size_t payloadLength) override
    {
        uint8_t cc = 0;
        uint8_t tid = 0;
        std::vector<bitfield8_t> types(8);
        auto rc = decode_get_tid_resp(responsePtr, payloadLength, &cc, &tid);
        if (rc != PLDM_SUCCESS || cc != PLDM_SUCCESS)
        {
            std::cerr << "Response Message Error: "
                      << "rc=" << rc << ",cc=" << (int)cc << "\n";
            return;
        }
        std::cout << "Parsed Response Msg: " << std::endl;
        std::cout << "TID : " << static_cast<uint32_t>(tid) << std::endl;
    }
};

void registerCommand(CLI::App& app)
{
    auto base = app.add_subcommand("base", "base type command");
    base->require_subcommand(1);

    auto getPLDMTypes =
        base->add_subcommand("GetPLDMTypes", "get pldm supported types");
    commands.push_back(
        std::make_unique<GetPLDMTypes>("base", "GetPLDMTypes", getPLDMTypes));

    auto getPLDMVersion =
        base->add_subcommand("GetPLDMVersion", "get version of a certain type");
    commands.push_back(std::make_unique<GetPLDMVersion>(
        "base", "GetPLDMVersion", getPLDMVersion));

    auto getPLDMTID = base->add_subcommand("GetTID", "get Terminus ID (TID)");
    commands.push_back(std::make_unique<GetTID>("base", "GetTID", getPLDMTID));
}

} // namespace base
} // namespace pldmtool
<|endoftext|>


<commit_before>/*
 * Model implementation
 */

#include "model.h"

/*
 * Constructs a new Model object using a text file
 * of lyrics. Models are adjacency list data structures
 * that map the frequency with which Words precede other Words
 */
Model::Model() {
  matrix = new std::vector<WordList>;
}

Model::~Model() {
  matrix->clear();
  delete matrix;
}

/*
 * Returns a pointer to the WordList
 * where <leader> is the base word.
 *
 * If <leader> is not found, returns a nullptr.
 */
WordList* Model::find(Word* leader) {
  std::vector<WordList>::iterator it;

  for(it = matrix->begin(); it != matrix->end(); ++it)
    if ((*it).get_base() == *leader)
      return &(*it);

  return nullptr;
}

/*
 * If Word already exists as base word in Model,
 * increments its frequency.
 *
 * Otherwise, instantiates a new WordList with
 * the Word as its base and appends to the model.
 */
void Model::addOrUpdate(Word* w) {
  WordList* list_ptr = find(w);

  if (list_ptr == nullptr) {
    WordList wl(*w);
    matrix->push_back(wl);
  }

  else
    (*list_ptr).get_base().incrementFrequency();
}

/*
 * Prints the model
 */
void Model::print(std::string filename) {
  std::ofstream f(filename);
  for (int i = 0; i < matrix->size(); i++) {
    f << i+1 << ": " << matrix->at(i) << std::endl;
  }
}

/*
 * Returns the number of WordLists in the model
 */
int Model::getSize() {
  return matrix->size();
}

WordList& Model::operator[](int i) {
  return (matrix->at(i));
}
<commit_msg>model destructor<commit_after>/*
 * Model implementation
 */

#include "model.h"

/*
 * Constructs a new Model object using a text file
 * of lyrics. Models are adjacency list data structures
 * that map the frequency with which Words precede other Words
 */
Model::Model() {
  matrix = new std::vector<WordList>;
}

Model::~Model() {
  while(matrix->size() != 0) {matrix->pop_back();}
  delete matrix;
}

/*
 * Returns a pointer to the WordList
 * where <leader> is the base word.
 *
 * If <leader> is not found, returns a nullptr.
 */
WordList* Model::find(Word* leader) {
  std::vector<WordList>::iterator it;

  for(it = matrix->begin(); it != matrix->end(); ++it)
    if ((*it).get_base() == *leader)
      return &(*it);

  return nullptr;
}

/*
 * If Word already exists as base word in Model,
 * increments its frequency.
 *
 * Otherwise, instantiates a new WordList with
 * the Word as its base and appends to the model.
 */
void Model::addOrUpdate(Word* w) {
  WordList* list_ptr = find(w);

  if (list_ptr == nullptr) {
    WordList wl(*w);
    matrix->push_back(wl);
  }

  else
    (*list_ptr).get_base().incrementFrequency();
}

/*
 * Prints the model
 */
void Model::print(std::string filename) {
  std::ofstream f(filename);
  for (int i = 0; i < matrix->size(); i++) {
    f << i+1 << ": " << matrix->at(i) << std::endl;
  }
}

/*
 * Returns the number of WordLists in the model
 */
int Model::getSize() {
  return matrix->size();
}

WordList& Model::operator[](int i) {
  return (matrix->at(i));
}
<|endoftext|>


<commit_before>/*
   Copyright 2009 Brain Research Institute, Melbourne, Australia

   Written by J-Donald Tournier, 03/09/09.

   This file is part of MRtrix.

   MRtrix 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 3 of the License, or
   (at your option) any later version.

   MRtrix 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 MRtrix.  If not, see <http://www.gnu.org/licenses/>.

 */

#include "image/handler/base.h"
#include "image/header.h"

namespace MR
{
  namespace Image
  {
    namespace Handler
    {
      Base::Base (const Image::Header& header) :
        name (header.name()),
        datatype (header.datatype()),
        segsize (Image::voxel_count (header)),
        is_new (false),
        writable (false) { }

      Base::~Base ()
      {
        close();
      }

      void Base::open ()
      {
        if (addresses.size())
          return;

        load();
        INFO ("image \"" + name + "\" loaded");
      }

      void Base::close ()
      {
        if (addresses.empty())
          return;

        unload();
        INFO ("image \"" + name + "\" unloaded");
        addresses.clear();
      }

      void Base::unload() { }

    }
  }
}

<commit_msg>Changed Image::Handler::Base messages from INFO to DEBUG<commit_after>/*
   Copyright 2009 Brain Research Institute, Melbourne, Australia

   Written by J-Donald Tournier, 03/09/09.

   This file is part of MRtrix.

   MRtrix 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 3 of the License, or
   (at your option) any later version.

   MRtrix 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 MRtrix.  If not, see <http://www.gnu.org/licenses/>.

 */

#include "image/handler/base.h"
#include "image/header.h"

namespace MR
{
  namespace Image
  {
    namespace Handler
    {
      Base::Base (const Image::Header& header) :
        name (header.name()),
        datatype (header.datatype()),
        segsize (Image::voxel_count (header)),
        is_new (false),
        writable (false) { }

      Base::~Base ()
      {
        close();
      }

      void Base::open ()
      {
        if (addresses.size())
          return;

        load();
        DEBUG ("image \"" + name + "\" loaded");
      }

      void Base::close ()
      {
        if (addresses.empty())
          return;

        unload();
        DEBUG ("image \"" + name + "\" unloaded");
        addresses.clear();
      }

      void Base::unload() { }

    }
  }
}

<|endoftext|>


<commit_before>//=====================================================================//
/*!	@file
	@brief	ファイル情報クラス
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "utils/file_info.hpp"
#include <dirent.h>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <iostream>

namespace utils {

	//-----------------------------------------------------------------//
	/*!
		@brief	ディレクトリーのファイルリストを作成
		@param[in]	root	ルート・パス
		@param[out]	list	ファイルリストを受け取るクラス
		@return リストの取得に失敗した場合「false」
	*/
	//-----------------------------------------------------------------//
	bool create_file_list(const std::string& root, file_infos& list)
	{
		if(root.empty()) return false;

#ifdef WIN32
		utils::wstring wsr;
		utf8_to_utf16(root, wsr);
		wchar_t* wtmp = new wchar_t[wsr.size() + 1];
		for(uint32_t i = 0; i < wsr.size(); ++i) {
			wtmp[i] = wsr[i];
		}
		wtmp[wsr.size()] = 0;
		_WDIR* dir = _wopendir(wtmp);
		delete[] wtmp;
#else
		DIR* dir = opendir(root.c_str());
#endif
		if(dir) {
#ifdef WIN32
			struct _wdirent* ent;
			while((ent = _wreaddir(dir)) != 0) {
				struct _stat st;
				utils::wstring fn;
				utf8_to_utf16(root, fn);
				fn += '/';
				{
					wchar_t wch;
					wchar_t* p = ent->d_name;
					while((wch = *p++) != 0) {
						fn += wch;
					}
				}
				wchar_t* wfn = new wchar_t[fn.size() + 1];
				for(uint32_t i = 0; i < fn.size(); ++i) {
					wfn[i] = fn[i];
				}
				wfn[fn.size()] = 0;
				if(ent->d_namlen > 0 && _wstat(wfn, &st) == 0) {
					bool d = S_ISDIR(st.st_mode);
					wstring ws;
					wchar_t wch;
					wchar_t* p = ent->d_name;
					while((wch = *p++) != 0) {
						ws += wch;
					}
					std::string s;
					utf16_to_utf8(ws, s);
					file_info info(s, d, st.st_size, st.st_mtime, st.st_mode);
					list.push_back(info);
				}
				delete[] wfn;
			}
			_wclosedir(dir);
#else
			struct dirent* ent;
			while((ent = readdir(dir)) != 0) {
				struct stat st;
				std::string fn = root;
				fn += '/';
				fn += ent->d_name;
				if(stat(fn.c_str(), &st) == 0) {
///					std::cout << fn << std::endl;
#ifdef __PPU__
					bool d = false;
					if(st.st_mode & CELL_FS_S_IFDIR) d = true;
#else
					bool d = S_ISDIR(st.st_mode);
#endif
					file_info info(ent->d_name, d, st.st_size, st.st_mtime, st.st_mode);
					list.push_back(info);
				}
			}
			closedir(dir);
#endif
			if(list.empty()) return false;
			else return true;
		} else {
			return false;
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	ファイル情報からフォーマットされた文字列を生成
		@param[in]	list	fileinfos クラス
		@param[in]	format	フォーマット（ディレクトリー出力専用形式）@n
							%s ---> ファイル名@n
							%n ---> ファイルサイズ@n
							%/ ---> ディレクトリーなら、'/'を追加
		@param[in]	dst		出力先列
	*/
	//-----------------------------------------------------------------//
	void create_format_file_infos(const file_infos& list, const std::string& format, utils::strings& dst)
	{
		BOOST_FOREACH(const file_info& fi, list) {
			if(format.empty()) {
				std::string s = fi.get_name();
				if(fi.is_directory()) s += '/';
				dst.push_back(s);
			} else {
				std::string s;
				char m = 0;
				BOOST_FOREACH(char c, format) {
					if(m) {
						if(c == '%') ;
						else if(c == 's' || c == 'S') {
							s += fi.get_name();
							c = 0;
						} else if(c == 'n') {
							s += boost::io::str(boost::format("%d") % fi.get_size());
							c = 0;
						} else if(c == '/') {
							if(!fi.is_directory()) c = 0;
						}
						m = 0;
					} else if(c == '%') {
						m = c;
						c = 0;
					}
					if(c) s += c;
				}
				dst.push_back(s);
			}
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	ファイル情報リストを拡張子フィルターで再構成
		@param[in]	src		fileinfos クラス
		@param[in]	filter	拡張子文字列
		@param[in]	dst		出力先列
	*/
	//-----------------------------------------------------------------//
	void filter_file_infos(const file_infos& src, const std::string& filter, file_infos& dst)
	{
		utils::strings ss;
		if(!filter.empty()) {
			ss = utils::split_text(filter, ",");
		}

		BOOST_FOREACH(const file_info& f, src) {
			bool add = false;
			if(f.is_directory() || ss.empty()) add = true;
			else {
				const std::string& n = f.get_name();
				BOOST_FOREACH(const std::string& s, ss) {
					const char* p = strrchr(n.c_str(), '.');
					if(p) {
						if(utils::no_capital_strcmp(std::string(p + 1), s) == 0) {
							add = true;
							break;
						}
					}
				}
			}
			if(add) dst.push_back(f);
		}
	}

}
<commit_msg>Update comment<commit_after>//=====================================================================//
/*!	@file
	@brief	ファイル情報クラス
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "utils/file_info.hpp"
#include <dirent.h>
#include <boost/foreach.hpp>
#include <boost/format.hpp>
#include <iostream>

namespace utils {

	//-----------------------------------------------------------------//
	/*!
		@brief	ディレクトリーのファイルリストを作成
		@param[in]	root	ルート・パス
		@param[out]	list	ファイルリストを受け取るクラス
		@return リストの取得に失敗した場合「false」
	*/
	//-----------------------------------------------------------------//
	bool create_file_list(const std::string& root, file_infos& list)
	{
		if(root.empty()) return false;

#ifdef WIN32
		utils::wstring wsr;
		utf8_to_utf16(root, wsr);
		wchar_t* wtmp = new wchar_t[wsr.size() + 1];
		for(uint32_t i = 0; i < wsr.size(); ++i) {
			wtmp[i] = wsr[i];
		}
		wtmp[wsr.size()] = 0;
		_WDIR* dir = _wopendir(wtmp);
		delete[] wtmp;
#else
		DIR* dir = opendir(root.c_str());
#endif
		if(dir) {
#ifdef WIN32
			struct _wdirent* ent;
			while((ent = _wreaddir(dir)) != 0) {
				struct _stat st;
				utils::wstring fn;
				utf8_to_utf16(root, fn);
				fn += '/';
				{
					wchar_t wch;
					wchar_t* p = ent->d_name;
					while((wch = *p++) != 0) {
						fn += wch;
					}
				}
				wchar_t* wfn = new wchar_t[fn.size() + 1];
				for(uint32_t i = 0; i < fn.size(); ++i) {
					wfn[i] = fn[i];
				}
				wfn[fn.size()] = 0;
				if(ent->d_namlen > 0 && _wstat(wfn, &st) == 0) {
					bool d = S_ISDIR(st.st_mode);
					wstring ws;
					wchar_t wch;
					wchar_t* p = ent->d_name;
					while((wch = *p++) != 0) {
						ws += wch;
					}
					std::string s;
					utf16_to_utf8(ws, s);
					file_info info(s, d, st.st_size, st.st_mtime, st.st_mode);
					list.push_back(info);
				}
				delete[] wfn;
			}
			_wclosedir(dir);
#else
			struct dirent* ent;
			while((ent = readdir(dir)) != 0) {
				struct stat st;
				std::string fn = root;
				fn += '/';
				fn += ent->d_name;
				if(stat(fn.c_str(), &st) == 0) {
///					std::cout << fn << std::endl;
#ifdef __PPU__
					bool d = false;
					if(st.st_mode & CELL_FS_S_IFDIR) d = true;
#else
					bool d = S_ISDIR(st.st_mode);
#endif
					file_info info(ent->d_name, d, st.st_size, st.st_mtime, st.st_mode);
					list.push_back(info);
				}
			}
			closedir(dir);
#endif
			return true;
		} else {
			return false;
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	ファイル情報からフォーマットされた文字列を生成
		@param[in]	list	fileinfos クラス
		@param[in]	format	フォーマット（ディレクトリー出力専用形式）@n
							%s ---> ファイル名@n
							%n ---> ファイルサイズ@n
							%/ ---> ディレクトリーなら、'/'を追加
		@param[in]	dst		出力先列
	*/
	//-----------------------------------------------------------------//
	void create_format_file_infos(const file_infos& list, const std::string& format, utils::strings& dst)
	{
		BOOST_FOREACH(const file_info& fi, list) {
			if(format.empty()) {
				std::string s = fi.get_name();
				if(fi.is_directory()) s += '/';
				dst.push_back(s);
			} else {
				std::string s;
				char m = 0;
				BOOST_FOREACH(char c, format) {
					if(m) {
						if(c == '%') ;
						else if(c == 's' || c == 'S') {
							s += fi.get_name();
							c = 0;
						} else if(c == 'n') {
							s += boost::io::str(boost::format("%d") % fi.get_size());
							c = 0;
						} else if(c == '/') {
							if(!fi.is_directory()) c = 0;
						}
						m = 0;
					} else if(c == '%') {
						m = c;
						c = 0;
					}
					if(c) s += c;
				}
				dst.push_back(s);
			}
		}
	}

	//-----------------------------------------------------------------//
	/*!
		@brief	ファイル情報リストを拡張子フィルターで再構成
		@param[in]	src		fileinfos クラス
		@param[in]	filter	拡張子文字列
		@param[in]	dst		出力先列
	*/
	//-----------------------------------------------------------------//
	void filter_file_infos(const file_infos& src, const std::string& filter, file_infos& dst)
	{
		utils::strings ss;
		if(!filter.empty()) {
			ss = utils::split_text(filter, ",");
		}

		BOOST_FOREACH(const file_info& f, src) {
			bool add = false;
			if(f.is_directory() || ss.empty()) add = true;
			else {
				const std::string& n = f.get_name();
				BOOST_FOREACH(const std::string& s, ss) {
					const char* p = strrchr(n.c_str(), '.');
					if(p) {
						if(utils::no_capital_strcmp(std::string(p + 1), s) == 0) {
							add = true;
							break;
						}
					}
				}
			}
			if(add) dst.push_back(f);
		}
	}
}
<|endoftext|>