<commit_before>#include "tests-base.hpp"

#include "utf8rewind.h"

struct CaseMappingEntry
{
	CaseMappingEntry()
		: codepoint(0)
	{
	}

	unicode_t codepoint;
	std::string uppercase;
	std::string lowercase;
	std::string titlecase;
	std::string name;
};

std::string CodepointToString(unicode_t codepoint)
{
	std::stringstream ss;
	ss << "U+" << std::setfill('0') << std::setw(8) << std::hex << codepoint;
	return ss.str();
}

std::string Utf8StringToCodepoints(const std::string& text)
{
	if (text == "")
	{
		return "U+00000000";
	}

	std::stringstream ss;
	ss << std::setfill('0') << std::hex;

	int32_t errors = 0;
	size_t size_in_bytes = utf8toutf32(text.c_str(), text.size(), nullptr, 0, &errors);
	if (size_in_bytes == 0 ||
		errors != 0)
	{
		return "";
	}

	unicode_t* result_utf32 = new unicode_t[size_in_bytes / sizeof(unicode_t)];

	utf8toutf32(text.c_str(), text.size(), result_utf32, size_in_bytes, &errors);

	for (size_t i = 0; i < size_in_bytes / sizeof(unicode_t); ++i)
	{
		if (i != 0)
		{
			ss << " ";
		}
		ss << "U+" << std::setw(8) << result_utf32[i];
	}

	delete [] result_utf32;

	return ss.str();
}

inline ::testing::AssertionResult CompareCaseMappingEntry(
	const char* expressionLeft, const char* expressionRight,
	const CaseMappingEntry& entryLeft, const CaseMappingEntry& entryRight
)
{
	if (entryLeft.codepoint == entryRight.codepoint &&
		entryLeft.uppercase == entryRight.uppercase &&
		entryLeft.lowercase == entryRight.lowercase &&
		entryLeft.titlecase == entryRight.titlecase)
	{
		return ::testing::AssertionSuccess();
	}
	else
	{
		::testing::AssertionResult result = ::testing::AssertionFailure();

		result << std::endl;
		result << entryLeft.name << std::endl;
		result << std::endl;

		if (entryLeft.codepoint != entryRight.codepoint)
		{
			result << std::endl;
			result << "[Codepoint]" << std::endl;
			result << "    Actual:    " << CodepointToString(entryRight.codepoint) << std::endl;
			result << "  Expected:    " << CodepointToString(entryLeft.codepoint) << std::endl;
			result << std::endl;
		}
		else
		{
			result << "[Codepoint]    " << CodepointToString(entryLeft.codepoint) << std::endl;
		}

		if (entryLeft.uppercase != entryRight.uppercase)
		{
			result << std::endl;
			result << "[Uppercase]" << std::endl;
			result << "    Actual:    " << Utf8StringToCodepoints(entryRight.uppercase) << std::endl;
			result << "  Expected:    " << Utf8StringToCodepoints(entryLeft.uppercase) << std::endl;
			result << std::endl;
		}
		else
		{
			result << "[Uppercase]    " << Utf8StringToCodepoints(entryLeft.uppercase) << std::endl;
		}

		if (entryLeft.lowercase != entryRight.lowercase)
		{
			result << std::endl;
			result << "[Lowercase]" << std::endl;
			result << "    Actual:    " << Utf8StringToCodepoints(entryRight.lowercase) << std::endl;
			result << "  Expected:    " << Utf8StringToCodepoints(entryLeft.lowercase) << std::endl;
			result << std::endl;
		}
		else
		{
			result << "[Lowercase]    " << Utf8StringToCodepoints(entryLeft.lowercase) << std::endl;
		}

		if (entryLeft.titlecase != entryRight.titlecase)
		{
			result << std::endl;
			result << "[Titlecase]" << std::endl;
			result << "    Actual:    " << Utf8StringToCodepoints(entryRight.titlecase) << std::endl;
			result << "  Expected:    " << Utf8StringToCodepoints(entryLeft.titlecase) << std::endl;
			result << std::endl;
		}
		else
		{
			result << "[Titlecase]    " << Utf8StringToCodepoints(entryLeft.titlecase) << std::endl;
		}

		return result;
	}
}

#define EXPECT_CASE_MAPPING(_line, _codepoint, _uppercase, _lowercase, _titlecase) { \
	CaseMappingEntry r = GetEntryAtLine(_line); \
	CaseMappingEntry e; \
	e.codepoint = _codepoint; \
	e.uppercase = _uppercase; \
	e.lowercase = _lowercase; \
	e.titlecase = _titlecase; \
	e.name = r.name; \
	EXPECT_PRED_FORMAT2(CompareCaseMappingEntry, e, r); \
}

class CaseMapping
	: public ::testing::Test
{

protected:

	void SetUp()
	{
		// load into file

		file.open("testdata/CaseMapping.txt", std::ios_base::in);
		ASSERT_TRUE(file.is_open());

		char line[1024];
		while (!file.eof())
		{
			file.getline(line, 1024);
			lines.push_back(line);
		}

		file.close();
	}

	void TearDown()
	{
		file.close();
	}

	CaseMappingEntry GetEntryAtLine(size_t lineIndex) const
	{
		CaseMappingEntry result;

		if (lineIndex >= lines.size())
		{
			return result;
		}

		const char* line_src = lines[lineIndex].c_str();

		if (line_src[0] == '#' ||
			strlen(line_src) == 0)
		{
			return result;
		}

		const char* src = line_src;
		size_t offset = 0;

		std::vector<unicode_t> source_codepoint = ReadCodepoints(src, offset);
		result.codepoint = source_codepoint[0];
		src += offset + 2;

		std::vector<unicode_t> source_uppercase = ReadCodepoints(src, offset);
		result.uppercase = CodepointsToUtf8(source_uppercase);
		src += offset + 2;

		std::vector<unicode_t> source_lowercase = ReadCodepoints(src, offset);
		result.lowercase = CodepointsToUtf8(source_lowercase);
		src += offset + 2;

		std::vector<unicode_t> source_titlecase = ReadCodepoints(src, offset);
		result.titlecase = CodepointsToUtf8(source_titlecase);
		src += offset + 2;

		src += 2;
		result.name = src;

		return result;
	}

	std::string CodepointsToUtf8(const std::vector<unicode_t>& codepoints) const
	{
		std::string result;

		int32_t errors = 0;
		size_t size_in_bytes = utf32toutf8(&codepoints[0], codepoints.size() * sizeof(unicode_t), nullptr, 0, &errors);

		if (size_in_bytes == 0 ||
			errors != 0)
		{
			return result;
		}

		char* result_utf8 = new char[size_in_bytes + 1];
		memset(result_utf8, 0, size_in_bytes + 1);

		utf32toutf8(&codepoints[0], codepoints.size() * sizeof(unicode_t), result_utf8, size_in_bytes + 1, &errors);

		result = std::string(result_utf8);

		delete [] result_utf8;

		return result;
	}

	std::vector<unicode_t> ReadCodepoints(const char* line, size_t& offset) const
	{
		std::vector<unicode_t> codepoints;

		const char* src_start = line;
		const char* src = src_start;

		while (*src != ';')
		{
			if (*src == ' ')
			{
				unicode_t found = StringToCodepoint(src_start, src - 1);
				codepoints.push_back(found);

				src++;

				src_start = src;
				src = src_start;
			}
			else
			{
				src++;
			}
		}

		unicode_t found = StringToCodepoint(src_start, src - 1);
		codepoints.push_back(found);

		offset = src - line;

		return codepoints;
	}

	unicode_t StringToCodepoint(const char* inputStart, const char* inputEnd) const
	{
		unicode_t result = 0;
		unicode_t position = 1;

		const char* src = inputEnd;
		while (src != inputStart)
		{
			unicode_t current = 0;

			if (*src >= '0' && *src <= '9')
			{
				current = *src - '0';
			}
			else if (*src >= 'A' && *src <= 'F')
			{
				current = (*src - 'A' + 10);
			}
			else if (*src >= 'a' && *src <= 'f')
			{
				current = (*src - 'a' + 10);
			}

			result += current * position;
			position <<= 4;

			src--;
		}

		return result;
	}

	std::fstream file;
	std::vector<std::string> lines;
	std::vector<CaseMappingEntry> entries;

};

TEST_F(CaseMapping, Parser)
{
	// first

	EXPECT_CASE_MAPPING(0, 0, "", "", "");

	// commented out

	EXPECT_CASE_MAPPING(11, 0, "", "", "");

	// empty

	EXPECT_CASE_MAPPING(14, 0, "", "", "");

	// valid

	EXPECT_CASE_MAPPING(991, 0x10b3, "\xE1\x82\xB3", "\xE2\xB4\x93", "\xE2\xB4\x93");
}

TEST_F(CaseMapping, Ascii)
{
	EXPECT_CASE_MAPPING(15, 0x41, "A", "a", "a");
	EXPECT_CASE_MAPPING(16, 0x42, "B", "b", "b");
	EXPECT_CASE_MAPPING(17, 0x43, "C", "c", "c");
	EXPECT_CASE_MAPPING(18, 0x44, "D", "d", "d");
	EXPECT_CASE_MAPPING(19, 0x45, "E", "e", "e");
	EXPECT_CASE_MAPPING(20, 0x46, "F", "f", "f");
	EXPECT_CASE_MAPPING(21, 0x47, "G", "g", "g");
	EXPECT_CASE_MAPPING(22, 0x48, "H", "h", "h");
	EXPECT_CASE_MAPPING(23, 0x49, "I", "i", "i");
	EXPECT_CASE_MAPPING(24, 0x4A, "J", "j", "j");
	EXPECT_CASE_MAPPING(25, 0x4B, "K", "k", "k");
	EXPECT_CASE_MAPPING(26, 0x4C, "L", "l", "l");
	EXPECT_CASE_MAPPING(27, 0x4D, "M", "m", "m");
	EXPECT_CASE_MAPPING(28, 0x4E, "N", "n", "n");
	EXPECT_CASE_MAPPING(29, 0x4F, "O", "o", "o");
	EXPECT_CASE_MAPPING(30, 0x50, "P", "p", "p");
	EXPECT_CASE_MAPPING(31, 0x51, "Q", "q", "q");
	EXPECT_CASE_MAPPING(32, 0x52, "R", "r", "r");
	EXPECT_CASE_MAPPING(33, 0x53, "S", "s", "s");
	EXPECT_CASE_MAPPING(34, 0x54, "T", "t", "t");
	EXPECT_CASE_MAPPING(35, 0x55, "U", "u", "u");
	EXPECT_CASE_MAPPING(36, 0x56, "V", "v", "v");
	EXPECT_CASE_MAPPING(37, 0x57, "W", "w", "w");
	EXPECT_CASE_MAPPING(38, 0x58, "X", "x", "x");
	EXPECT_CASE_MAPPING(39, 0x59, "Y", "y", "y");
	EXPECT_CASE_MAPPING(40, 0x5A, "Z", "z", "z");
}<commit_msg>integration-casemapping: Refactored tests to use a singleton database.<commit_after>#include "tests-base.hpp"

#include "utf8rewind.h"

struct CaseMappingEntry
{
	CaseMappingEntry()
		: codepoint(0)
	{
	}

	unicode_t codepoint;
	std::string uppercase;
	std::string lowercase;
	std::string titlecase;
	std::string name;
};

class CaseMappingDatabase
{

public:

	static CaseMappingDatabase& Get()
	{
		static CaseMappingDatabase instance;
		return instance;
	}

	bool IsValid() const { return m_Database.size() > 0; }

	CaseMappingEntry* Find(unicode_t codepoint) const
	{
		std::map<unicode_t, CaseMappingEntry*>::const_iterator found = m_Database.find(codepoint);
		if (found != m_Database.end())
		{
			return found->second;
		}

		return nullptr;
	}

private:

	CaseMappingDatabase()
	{
		std::fstream file("testdata/CaseMapping.txt", std::ios_base::in);
		if (file.is_open())
		{
			char line[1024];
			while (!file.eof())
			{
				file.getline(line, 1024);

				CaseMappingEntry* entry = ParseLine(line);
				if (entry != nullptr)
				{
					m_Database.insert(std::make_pair(entry->codepoint, entry));
				}
			}

			file.close();
		}
	}

	CaseMappingEntry* ParseLine(const char* line) const
	{
		const char* line_src = line;

		if (line_src[0] == '#' ||
			strlen(line_src) == 0)
		{
			return nullptr;
		}

		CaseMappingEntry* entry = new CaseMappingEntry;

		const char* src = line_src;
		size_t offset = 0;

		std::vector<unicode_t> source_codepoint = ReadCodepoints(src, offset);
		entry->codepoint = source_codepoint[0];
		src += offset + 2;

		std::vector<unicode_t> source_uppercase = ReadCodepoints(src, offset);
		entry->uppercase = CodepointsToUtf8(source_uppercase);
		src += offset + 2;

		std::vector<unicode_t> source_lowercase = ReadCodepoints(src, offset);
		entry->lowercase = CodepointsToUtf8(source_lowercase);
		src += offset + 2;

		std::vector<unicode_t> source_titlecase = ReadCodepoints(src, offset);
		entry->titlecase = CodepointsToUtf8(source_titlecase);
		src += offset + 2;

		src += 2;
		entry->name = src;

		return entry;
	}

	std::string CodepointsToUtf8(const std::vector<unicode_t>& codepoints) const
	{
		std::string result;

		int32_t errors = 0;
		size_t size_in_bytes = utf32toutf8(&codepoints[0], codepoints.size() * sizeof(unicode_t), nullptr, 0, &errors);

		if (size_in_bytes == 0 ||
			errors != 0)
		{
			return result;
		}

		char* result_utf8 = new char[size_in_bytes + 1];
		memset(result_utf8, 0, size_in_bytes + 1);

		utf32toutf8(&codepoints[0], codepoints.size() * sizeof(unicode_t), result_utf8, size_in_bytes + 1, &errors);

		result = std::string(result_utf8);

		delete [] result_utf8;

		return result;
	}

	std::vector<unicode_t> ReadCodepoints(const char* line, size_t& offset) const
	{
		std::vector<unicode_t> codepoints;

		const char* src_start = line;
		const char* src = src_start;

		while (*src != ';')
		{
			if (*src == ' ')
			{
				unicode_t found = StringToCodepoint(src_start, src - 1);
				codepoints.push_back(found);

				src++;

				src_start = src;
				src = src_start;
			}
			else
			{
				src++;
			}
		}

		unicode_t found = StringToCodepoint(src_start, src - 1);
		codepoints.push_back(found);

		offset = src - line;

		return codepoints;
	}

	unicode_t StringToCodepoint(const char* inputStart, const char* inputEnd) const
	{
		unicode_t result = 0;
		unicode_t position = 1;

		const char* src = inputEnd;
		while (src != inputStart)
		{
			unicode_t current = 0;

			if (*src >= '0' && *src <= '9')
			{
				current = *src - '0';
			}
			else if (*src >= 'A' && *src <= 'F')
			{
				current = (*src - 'A' + 10);
			}
			else if (*src >= 'a' && *src <= 'f')
			{
				current = (*src - 'a' + 10);
			}

			result += current * position;
			position <<= 4;

			src--;
		}

		return result;
	}

	std::map<unicode_t, CaseMappingEntry*> m_Database;
};

std::string CodepointToString(unicode_t codepoint)
{
	std::stringstream ss;
	ss << "U+" << std::setfill('0') << std::setw(8) << std::hex << codepoint;
	return ss.str();
}

std::string CodepointToUppercaseString(unicode_t codepoint)
{
	int32_t errors = 0;

	char input_utf8[16] = { 0 };
	utf32toutf8(&codepoint, sizeof(unicode_t), input_utf8, 16, &errors);
	if (errors != 0)
	{
		return "";
	}

	char result_upper[256] = { 0 };
	utf8toupper(input_utf8, strlen(input_utf8), result_upper, 256, &errors);
	if (errors != 0)
	{
		return "";
	}

	return result_upper;
}

std::string CodepointToLowercaseString(unicode_t codepoint)
{
	int32_t errors = 0;

	char input_utf8[16] = { 0 };
	utf32toutf8(&codepoint, sizeof(unicode_t), input_utf8, 16, &errors);
	if (errors != 0)
	{
		return "";
	}

	// TODO: Implement
	return input_utf8;
}

std::string CodepointToTitlecaseString(unicode_t codepoint)
{
	int32_t errors = 0;

	char input_utf8[16] = { 0 };
	utf32toutf8(&codepoint, sizeof(unicode_t), input_utf8, 16, &errors);
	if (errors != 0)
	{
		return "";
	}

	// TODO: Implement
	return input_utf8;
}

std::string Utf8StringToCodepoints(const std::string& text)
{
	if (text == "")
	{
		return "U+00000000";
	}

	std::stringstream ss;
	ss << std::setfill('0') << std::hex;

	int32_t errors = 0;
	size_t size_in_bytes = utf8toutf32(text.c_str(), text.size(), nullptr, 0, &errors);
	if (size_in_bytes == 0 ||
		errors != 0)
	{
		return "";
	}

	unicode_t* result_utf32 = new unicode_t[size_in_bytes / sizeof(unicode_t)];

	utf8toutf32(text.c_str(), text.size(), result_utf32, size_in_bytes, &errors);

	for (size_t i = 0; i < size_in_bytes / sizeof(unicode_t); ++i)
	{
		if (i != 0)
		{
			ss << " ";
		}
		ss << "U+" << std::setw(8) << result_utf32[i];
	}

	delete [] result_utf32;

	return ss.str();
}

std::string Utf8StringPrintable(const std::string& text)
{
	std::stringstream ss;
	ss << std::setfill('0') << std::hex;

	for (std::string::const_iterator it = text.begin(); it != text.end(); ++it)
	{
		if (*it < 0x20 || *it > 0x7F)
		{
			ss << "\\x" << std::setw(2) << *it;
		}
		else
		{
			ss << std::string(it, it + 1);
		}
	}

	return ss.str();
}

inline ::testing::AssertionResult CompareCaseMappingEntry(
	const char* expressionLeft, const char* expressionRight,
	const CaseMappingEntry& entryLeft, const CaseMappingEntry& entryRight
)
{
	if (entryLeft.codepoint == entryRight.codepoint &&
		entryLeft.uppercase == entryRight.uppercase &&
		entryLeft.lowercase == entryRight.lowercase &&
		entryLeft.titlecase == entryRight.titlecase)
	{
		return ::testing::AssertionSuccess();
	}
	else
	{
		::testing::AssertionResult result = ::testing::AssertionFailure();

		result << std::endl;
		result << entryLeft.name << std::endl;
		result << std::endl;

		if (entryLeft.codepoint != entryRight.codepoint)
		{
			result << std::endl;
			result << "[Codepoint]" << std::endl;
			result << "    Actual:    " << CodepointToString(entryRight.codepoint) << std::endl;
			result << "  Expected:    " << CodepointToString(entryLeft.codepoint) << std::endl;
			result << std::endl;
		}
		else
		{
			result << "[Codepoint]    " << CodepointToString(entryLeft.codepoint) << std::endl;
		}

		if (entryLeft.uppercase != entryRight.uppercase)
		{
			result << std::endl;
			result << "[Uppercase]" << std::endl;
			result << "    Actual:    " << Utf8StringPrintable(entryRight.uppercase) << " (" << Utf8StringToCodepoints(entryRight.uppercase) << ")" << std::endl;
			result << "  Expected:    " << Utf8StringPrintable(entryLeft.uppercase) << " (" << Utf8StringToCodepoints(entryLeft.uppercase) << ")" << std::endl;
			result << std::endl;
		}
		else
		{
			result << "[Uppercase]    " << Utf8StringPrintable(entryLeft.uppercase) << " (" << Utf8StringToCodepoints(entryLeft.uppercase) << ")" << std::endl;
		}

		if (entryLeft.lowercase != entryRight.lowercase)
		{
			result << std::endl;
			result << "[Lowercase]" << std::endl;
			result << "    Actual:    " << Utf8StringPrintable(entryRight.lowercase) << " (" << Utf8StringToCodepoints(entryRight.lowercase) << ")" << std::endl;
			result << "  Expected:    " << Utf8StringPrintable(entryLeft.lowercase) << " (" << Utf8StringToCodepoints(entryLeft.lowercase) << ")" << std::endl;
			result << std::endl;
		}
		else
		{
			result << "[Lowercase]    " << Utf8StringPrintable(entryLeft.lowercase) << " (" << Utf8StringToCodepoints(entryLeft.lowercase) << ")" << std::endl;
		}

		if (entryLeft.titlecase != entryRight.titlecase)
		{
			result << std::endl;
			result << "[Titlecase]" << std::endl;
			result << "    Actual:    " << Utf8StringPrintable(entryRight.titlecase) << " (" << Utf8StringToCodepoints(entryRight.titlecase) << ")" << std::endl;
			result << "  Expected:    " << Utf8StringPrintable(entryLeft.titlecase) << " (" << Utf8StringToCodepoints(entryLeft.titlecase) << ")" << std::endl;
			result << std::endl;
		}
		else
		{
			result << "[Titlecase]    " << Utf8StringPrintable(entryLeft.titlecase) << " (" << Utf8StringToCodepoints(entryLeft.titlecase) << ")" << std::endl;
		}

		return result;
	}
}

#define CHECK_CASE_MAPPING(_codepoint) { \
	CaseMappingEntry* a = CaseMappingDatabase::Get().Find(_codepoint); \
	ASSERT_NE(nullptr, a); \
	CaseMappingEntry e; \
	e.codepoint = _codepoint; \
	e.lowercase = CodepointToLowercaseString(_codepoint); \
	e.uppercase = CodepointToUppercaseString(_codepoint); \
	e.titlecase = a->titlecase; \
	e.name = a->name; \
	EXPECT_PRED_FORMAT2(CompareCaseMappingEntry, e, *a); \
}

class CaseMapping
	: public ::testing::Test
{

protected:

	void SetUp()
	{
		ASSERT_TRUE(CaseMappingDatabase::Get().IsValid());
	}

	void TearDown()
	{
	}

};

TEST_F(CaseMapping, AsciiUppercase)
{
	CHECK_CASE_MAPPING(0x61);
	CHECK_CASE_MAPPING(0x62);
	CHECK_CASE_MAPPING(0x63);
	CHECK_CASE_MAPPING(0x64);
	CHECK_CASE_MAPPING(0x65);
	CHECK_CASE_MAPPING(0x66);
	CHECK_CASE_MAPPING(0x67);
	CHECK_CASE_MAPPING(0x68);
	CHECK_CASE_MAPPING(0x69);
	CHECK_CASE_MAPPING(0x6A);
	CHECK_CASE_MAPPING(0x6B);
	CHECK_CASE_MAPPING(0x6C);
	CHECK_CASE_MAPPING(0x6D);
	CHECK_CASE_MAPPING(0x6E);
	CHECK_CASE_MAPPING(0x6F);
	CHECK_CASE_MAPPING(0x70);
	CHECK_CASE_MAPPING(0x71);
	CHECK_CASE_MAPPING(0x72);
	CHECK_CASE_MAPPING(0x73);
	CHECK_CASE_MAPPING(0x74);
	CHECK_CASE_MAPPING(0x75);
	CHECK_CASE_MAPPING(0x76);
	CHECK_CASE_MAPPING(0x77);
	CHECK_CASE_MAPPING(0x78);
	CHECK_CASE_MAPPING(0x79);
	CHECK_CASE_MAPPING(0x7A);
}

TEST_F(CaseMapping, AsciiLowercase)
{
	CHECK_CASE_MAPPING(0x41);
	CHECK_CASE_MAPPING(0x42);
	CHECK_CASE_MAPPING(0x43);
	CHECK_CASE_MAPPING(0x44);
	CHECK_CASE_MAPPING(0x45);
	CHECK_CASE_MAPPING(0x46);
	CHECK_CASE_MAPPING(0x47);
	CHECK_CASE_MAPPING(0x48);
	CHECK_CASE_MAPPING(0x49);
	CHECK_CASE_MAPPING(0x4A);
	CHECK_CASE_MAPPING(0x4B);
	CHECK_CASE_MAPPING(0x4C);
	CHECK_CASE_MAPPING(0x4D);
	CHECK_CASE_MAPPING(0x4E);
	CHECK_CASE_MAPPING(0x4F);
	CHECK_CASE_MAPPING(0x50);
	CHECK_CASE_MAPPING(0x51);
	CHECK_CASE_MAPPING(0x52);
	CHECK_CASE_MAPPING(0x53);
	CHECK_CASE_MAPPING(0x54);
	CHECK_CASE_MAPPING(0x55);
	CHECK_CASE_MAPPING(0x56);
	CHECK_CASE_MAPPING(0x57);
	CHECK_CASE_MAPPING(0x58);
	CHECK_CASE_MAPPING(0x59);
	CHECK_CASE_MAPPING(0x5A);
}<|endoftext|>


<commit_before>// Copyright (c) 2010 - 2013 Leap Motion. All rights reserved. Proprietary and confidential.
#include "stdafx.h"
#include "CoreThread.h"
#include "CoreContext.h"
#include "ThreadStatusMaintainer.h"
#include <boost/thread.hpp>

CoreThread::CoreThread(const char* pName):
  ContextMember(pName),
  m_stop(false),
  m_ready(false),
  m_running(false),
  m_completed(false),
  m_canAccept(false)
{
}

void CoreThread::DoRun(void) {
  ASSERT(m_running);

  // Make our own session current before we do anything else:
  CurrentContextPusher pusher(GetContext());

  // Set the thread name no matter what:
  if(GetName())
    SetCurrentThreadName();

  // Now we wait for the thread to be good to go:
  DelayUntilReady();
  try {
    Run();
  } catch(dispatch_aborted_exception&) {
    // Okay, this is fine, a dispatcher is terminating--this is a normal way to
    // end a dispatcher loop.
  } catch(...) {
    try {
      // Ask that the enclosing context filter this exception, if possible:
      GetContext()->FilterException();
    } catch(...) {
      // Generic exception, unhandled, we can't print anything off.
      CoreContext::DebugPrintCurrentExceptionInformation();
    }

    // Signal shutdown on the enclosing context
    GetContext()->SignalShutdown();
  }

  // Unconditionally shut off dispatch delivery:
  RejectDispatchDelivery();

  // Notify everyone that we're completed:
  boost::lock_guard<boost::mutex> lk(m_lock);
  m_completed = true;
  m_running = false;
  m_stateCondition.notify_all();

  // No longer running, we MUST release the thread pointer to ensure proper teardown
  m_thisThread.detach();

  // And finally, perform a manual notification of teardown listeners
  NotifyTeardownListeners();
}

bool CoreThread::ShouldStop(void) const {
  shared_ptr<CoreContext> context = ContextMember::GetContext();
  return m_stop || !context || context->ShouldStop();
}

void CoreThread::ThreadSleep(long millisecond) {
  boost::this_thread::sleep(boost::posix_time::milliseconds(millisecond));
}

bool CoreThread::DelayUntilCanAccept(void) {
  boost::unique_lock<boost::mutex> lk(m_lock);
  m_stateCondition.wait(lk, [this] {return ShouldStop() || CanAccept();});
  return !ShouldStop();
}

bool CoreThread::Start(void) {
  std::shared_ptr<CoreContext> context = m_context.lock();
  if(!context)
    return false;

  {
    boost::lock_guard<boost::mutex> lk(m_lock);
    if(m_running)
      // Already running, short-circuit
      return true;

    // Currently running:
    m_running = true;
    m_stateCondition.notify_all();
  }

  // Kick off a thread and return here
  m_thisThread = boost::thread(ThreadStatusMaintainer(this, context));
  return true;
}

void CoreThread::Run() {
  AcceptDispatchDelivery();
  while(!ShouldStop())
    WaitForEvent();

  // If we are asked to rundown while we still have elements in our dispatch queue,
  // we must try to process them:
  DispatchAllEvents();
}
<commit_msg>Adding a teardown listener to the camera viewer which will propagate teardown to the pipeline context<commit_after>// Copyright (c) 2010 - 2013 Leap Motion. All rights reserved. Proprietary and confidential.
#include "stdafx.h"
#include "CoreThread.h"
#include "CoreContext.h"
#include "ThreadStatusMaintainer.h"
#include <boost/thread.hpp>

CoreThread::CoreThread(const char* pName):
  ContextMember(pName),
  m_stop(false),
  m_ready(false),
  m_running(false),
  m_completed(false),
  m_canAccept(false)
{
}

void CoreThread::DoRun(void) {
  ASSERT(m_running);

  // Make our own session current before we do anything else:
  CurrentContextPusher pusher(GetContext());

  // Set the thread name no matter what:
  if(GetName())
    SetCurrentThreadName();

  // Now we wait for the thread to be good to go:
  DelayUntilReady();
  try {
    Run();
  } catch(dispatch_aborted_exception&) {
    // Okay, this is fine, a dispatcher is terminating--this is a normal way to
    // end a dispatcher loop.
  } catch(...) {
    try {
      // Ask that the enclosing context filter this exception, if possible:
      GetContext()->FilterException();
    } catch(...) {
      // Generic exception, unhandled, we can't print anything off.
      CoreContext::DebugPrintCurrentExceptionInformation();
    }

    // Signal shutdown on the enclosing context
    GetContext()->SignalShutdown();
  }

  // Unconditionally shut off dispatch delivery:
  RejectDispatchDelivery();

  // Notify everyone that we're completed:
  boost::lock_guard<boost::mutex> lk(m_lock);
  m_completed = true;
  m_running = false;
  m_stateCondition.notify_all();

  // Perform a manual notification of teardown listeners
  NotifyTeardownListeners();

  // No longer running, we MUST release the thread pointer to ensure proper teardown
  m_thisThread.detach();
}

bool CoreThread::ShouldStop(void) const {
  shared_ptr<CoreContext> context = ContextMember::GetContext();
  return m_stop || !context || context->ShouldStop();
}

void CoreThread::ThreadSleep(long millisecond) {
  boost::this_thread::sleep(boost::posix_time::milliseconds(millisecond));
}

bool CoreThread::DelayUntilCanAccept(void) {
  boost::unique_lock<boost::mutex> lk(m_lock);
  m_stateCondition.wait(lk, [this] {return ShouldStop() || CanAccept();});
  return !ShouldStop();
}

bool CoreThread::Start(void) {
  std::shared_ptr<CoreContext> context = m_context.lock();
  if(!context)
    return false;

  {
    boost::lock_guard<boost::mutex> lk(m_lock);
    if(m_running)
      // Already running, short-circuit
      return true;

    // Currently running:
    m_running = true;
    m_stateCondition.notify_all();
  }

  // Kick off a thread and return here
  m_thisThread = boost::thread(ThreadStatusMaintainer(this, context));
  return true;
}

void CoreThread::Run() {
  AcceptDispatchDelivery();
  while(!ShouldStop())
    WaitForEvent();

  // If we are asked to rundown while we still have elements in our dispatch queue,
  // we must try to process them:
  DispatchAllEvents();
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2004-2014 ZNC, see the NOTICE file for details.
 * Copyright (C) 2006-2007, CNU <bshalm@broadpark.no> (http://cnu.dieplz.net/znc)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <znc/FileUtils.h>
#include <znc/User.h>
#include <znc/IRCNetwork.h>
#include <znc/Chan.h>
#include <znc/Server.h>

using std::vector;

class CLogMod: public CModule {
public:
	MODCONSTRUCTOR(CLogMod)
	{
		m_bSanitize = false;
	}

	void PutLog(const CString& sLine, const CString& sWindow = "status");
	void PutLog(const CString& sLine, const CChan& Channel);
	void PutLog(const CString& sLine, const CNick& Nick);
	CString GetServer();

	virtual bool OnLoad(const CString& sArgs, CString& sMessage);
	virtual void OnIRCConnected();
	virtual void OnIRCDisconnected();
	virtual EModRet OnBroadcast(CString& sMessage);

	virtual void OnRawMode2(const CNick* pOpNick, CChan& Channel, const CString& sModes, const CString& sArgs);
	virtual void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage);
	virtual void OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans);
	virtual void OnJoin(const CNick& Nick, CChan& Channel);
	virtual void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage);
	virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans);
	virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic);

	/* notices */
	virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage);
	virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage);
	virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage);

	/* actions */
	virtual EModRet OnUserAction(CString& sTarget, CString& sMessage);
	virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage);
	virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage);

	/* msgs */
	virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage);
	virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage);
	virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage);

private:
	CString                 m_sLogPath;
	bool                    m_bSanitize;
};

void CLogMod::PutLog(const CString& sLine, const CString& sWindow /*= "Status"*/)
{
	CString sPath;
	time_t curtime;

	time(&curtime);
	// Generate file name
	sPath = CUtils::FormatTime(curtime, m_sLogPath, m_pUser->GetTimezone());
	if (sPath.empty())
	{
		DEBUG("Could not format log path [" << sPath << "]");
		return;
	}

	// TODO: Properly handle IRC case mapping
	// $WINDOW has to be handled last, since it can contain %
	sPath.Replace("$USER", CString((m_pUser ? m_pUser->GetUserName() : "UNKNOWN")).AsLower());
	sPath.Replace("$NETWORK", CString((m_pNetwork ? m_pNetwork->GetName() : "znc")).AsLower());
	sPath.Replace("$WINDOW", CString(sWindow.Replace_n("/", "-").Replace_n("\\", "-")).AsLower());

	// Check if it's allowed to write in this specific path
	sPath = CDir::CheckPathPrefix(GetSavePath(), sPath);
	if (sPath.empty())
	{
		DEBUG("Invalid log path ["<<m_sLogPath<<"].");
		return;
	}

	CFile LogFile(sPath);
	CString sLogDir = LogFile.GetDir();
	struct stat ModDirInfo;
	CFile::GetInfo(GetSavePath(), ModDirInfo);
	if (!CFile::Exists(sLogDir)) CDir::MakeDir(sLogDir, ModDirInfo.st_mode);
	if (LogFile.Open(O_WRONLY | O_APPEND | O_CREAT))
	{
		LogFile.Write(CUtils::FormatTime(curtime, "[%H:%M:%S] ", m_pUser->GetTimezone()) + (m_bSanitize ? sLine.StripControls_n() : sLine) + "\n");
	} else
		DEBUG("Could not open log file [" << sPath << "]: " << strerror(errno));
}

void CLogMod::PutLog(const CString& sLine, const CChan& Channel)
{
	PutLog(sLine, Channel.GetName());
}

void CLogMod::PutLog(const CString& sLine, const CNick& Nick)
{
	PutLog(sLine, Nick.GetNick());
}

CString CLogMod::GetServer()
{
	CServer* pServer = m_pNetwork->GetCurrentServer();
	CString sSSL;

	if (!pServer)
		return "(no server)";

	if (pServer->IsSSL())
		sSSL = "+";
	return pServer->GetName() + " " + sSSL + CString(pServer->GetPort());
}

bool CLogMod::OnLoad(const CString& sArgs, CString& sMessage)
{
	size_t uIndex = 0;
	if (sArgs.Token(0).Equals("-sanitize"))
	{
		m_bSanitize = true;
		++uIndex;
	}

	// Use load parameter as save path
	m_sLogPath = sArgs.Token(uIndex);

	// Add default filename to path if it's a folder
	if (GetType() == CModInfo::UserModule) {
		if (m_sLogPath.Right(1) == "/" || m_sLogPath.find("$WINDOW") == CString::npos || m_sLogPath.find("$NETWORK") == CString::npos) {
			if (!m_sLogPath.empty()) {
				m_sLogPath += "/";
			}
			m_sLogPath += "$NETWORK/$WINDOW/%Y-%m-%d.log";
		}
	} else if (GetType() == CModInfo::NetworkModule) {
		if (m_sLogPath.Right(1) == "/" || m_sLogPath.find("$WINDOW") == CString::npos) {
			if (!m_sLogPath.empty()) {
				m_sLogPath += "/";
			}
			m_sLogPath += "$WINDOW/%Y-%m-%d.log";
		}
	} else {
		if (m_sLogPath.Right(1) == "/" || m_sLogPath.find("$USER") == CString::npos || m_sLogPath.find("$WINDOW") == CString::npos || m_sLogPath.find("$NETWORK") == CString::npos) {
			if (!m_sLogPath.empty()) {
				m_sLogPath += "/";
			}
			m_sLogPath += "$USER/$NETWORK/$WINDOW/%Y-%m-%d.log";
		}
	}

	// Check if it's allowed to write in this path in general
	m_sLogPath = CDir::CheckPathPrefix(GetSavePath(), m_sLogPath);
	if (m_sLogPath.empty())
	{
		sMessage = "Invalid log path ["+m_sLogPath+"].";
		return false;
	} else {
		sMessage = "Logging to ["+m_sLogPath+"].";
		return true;
	}
}

void CLogMod::OnIRCConnected()
{
	PutLog("Connected to IRC (" + GetServer() + ")");
}

void CLogMod::OnIRCDisconnected()
{
	PutLog("Disconnected from IRC (" + GetServer() + ")");
}

CModule::EModRet CLogMod::OnBroadcast(CString& sMessage)
{
	PutLog("Broadcast: " + sMessage);
	return CONTINUE;
}

void CLogMod::OnRawMode2(const CNick* pOpNick, CChan& Channel, const CString& sModes, const CString& sArgs)
{
	const CString sNick = pOpNick ? pOpNick->GetNick() : "Server";
	PutLog("*** " + sNick + " sets mode: " + sModes + " " + sArgs, Channel);
}

void CLogMod::OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage)
{
	PutLog("*** " + sKickedNick + " was kicked by " + OpNick.GetNick() + " (" + sMessage + ")", Channel);
}

void CLogMod::OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans)
{
	for (std::vector<CChan*>::const_iterator pChan = vChans.begin(); pChan != vChans.end(); ++pChan)
		PutLog("*** Quits: " + Nick.GetNick() + " (" + Nick.GetIdent() + "@" + Nick.GetHost() + ") (" + sMessage + ")", **pChan);
}

void CLogMod::OnJoin(const CNick& Nick, CChan& Channel)
{
	PutLog("*** Joins: " + Nick.GetNick() + " (" + Nick.GetIdent() + "@" + Nick.GetHost() + ")", Channel);
}

void CLogMod::OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage)
{
	PutLog("*** Parts: " + Nick.GetNick() + " (" + Nick.GetIdent() + "@" + Nick.GetHost() + ") (" + sMessage + ")", Channel);
}

void CLogMod::OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans)
{
	for (std::vector<CChan*>::const_iterator pChan = vChans.begin(); pChan != vChans.end(); ++pChan)
		PutLog("*** " + OldNick.GetNick() + " is now known as " + sNewNick, **pChan);
}

CModule::EModRet CLogMod::OnTopic(CNick& Nick, CChan& Channel, CString& sTopic)
{
	PutLog("*** " + Nick.GetNick() + " changes topic to '" + sTopic + "'", Channel);
	return CONTINUE;
}

/* notices */
CModule::EModRet CLogMod::OnUserNotice(CString& sTarget, CString& sMessage)
{
	if (m_pNetwork) {
		PutLog("-" + m_pNetwork->GetCurNick() + "- " + sMessage, sTarget);
	}

	return CONTINUE;
}

CModule::EModRet CLogMod::OnPrivNotice(CNick& Nick, CString& sMessage)
{
	PutLog("-" + Nick.GetNick() + "- " + sMessage, Nick);
	return CONTINUE;
}

CModule::EModRet CLogMod::OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage)
{
	PutLog("-" + Nick.GetNick() + "- " + sMessage, Channel);
	return CONTINUE;
}

/* actions */
CModule::EModRet CLogMod::OnUserAction(CString& sTarget, CString& sMessage)
{
	if (m_pNetwork) {
		PutLog("* " + m_pNetwork->GetCurNick() + " " + sMessage, sTarget);
	}

	return CONTINUE;
}

CModule::EModRet CLogMod::OnPrivAction(CNick& Nick, CString& sMessage)
{
	PutLog("* " + Nick.GetNick() + " " + sMessage, Nick);
	return CONTINUE;
}

CModule::EModRet CLogMod::OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage)
{
	PutLog("* " + Nick.GetNick() + " " + sMessage, Channel);
	return CONTINUE;
}

/* msgs */
CModule::EModRet CLogMod::OnUserMsg(CString& sTarget, CString& sMessage)
{
	if (m_pNetwork) {
		PutLog("<" + m_pNetwork->GetCurNick() + "> " + sMessage, sTarget);
	}

	return CONTINUE;
}

CModule::EModRet CLogMod::OnPrivMsg(CNick& Nick, CString& sMessage)
{
	PutLog("<" + Nick.GetNick() + "> " + sMessage, Nick);
	return CONTINUE;
}

CModule::EModRet CLogMod::OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage)
{
	PutLog("<" + Nick.GetNick() + "> " + sMessage, Channel);
	return CONTINUE;
}

template<> void TModInfo<CLogMod>(CModInfo& Info) {
	Info.AddType(CModInfo::NetworkModule);
	Info.AddType(CModInfo::GlobalModule);
	Info.SetHasArgs(true);
	Info.SetArgsHelpText("[-sanitize] Optional path where to store logs.");
	Info.SetWikiPage("log");
}

USERMODULEDEFS(CLogMod, "Write IRC logs.")
<commit_msg>log: Add -timestamp option<commit_after>/*
 * Copyright (C) 2004-2014 ZNC, see the NOTICE file for details.
 * Copyright (C) 2006-2007, CNU <bshalm@broadpark.no> (http://cnu.dieplz.net/znc)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <znc/FileUtils.h>
#include <znc/User.h>
#include <znc/IRCNetwork.h>
#include <znc/Chan.h>
#include <znc/Server.h>

using std::vector;

class CLogMod: public CModule {
public:
	MODCONSTRUCTOR(CLogMod)
	{
		m_bSanitize = false;
	}

	void PutLog(const CString& sLine, const CString& sWindow = "status");
	void PutLog(const CString& sLine, const CChan& Channel);
	void PutLog(const CString& sLine, const CNick& Nick);
	CString GetServer();

	virtual bool OnLoad(const CString& sArgs, CString& sMessage);
	virtual void OnIRCConnected();
	virtual void OnIRCDisconnected();
	virtual EModRet OnBroadcast(CString& sMessage);

	virtual void OnRawMode2(const CNick* pOpNick, CChan& Channel, const CString& sModes, const CString& sArgs);
	virtual void OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage);
	virtual void OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans);
	virtual void OnJoin(const CNick& Nick, CChan& Channel);
	virtual void OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage);
	virtual void OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans);
	virtual EModRet OnTopic(CNick& Nick, CChan& Channel, CString& sTopic);

	/* notices */
	virtual EModRet OnUserNotice(CString& sTarget, CString& sMessage);
	virtual EModRet OnPrivNotice(CNick& Nick, CString& sMessage);
	virtual EModRet OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage);

	/* actions */
	virtual EModRet OnUserAction(CString& sTarget, CString& sMessage);
	virtual EModRet OnPrivAction(CNick& Nick, CString& sMessage);
	virtual EModRet OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage);

	/* msgs */
	virtual EModRet OnUserMsg(CString& sTarget, CString& sMessage);
	virtual EModRet OnPrivMsg(CNick& Nick, CString& sMessage);
	virtual EModRet OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage);

private:
	CString                 m_sLogPath;
	CString                 m_sTimestamp;
	bool                    m_bSanitize;
};

void CLogMod::PutLog(const CString& sLine, const CString& sWindow /*= "Status"*/)
{
	CString sPath;
	time_t curtime;

	time(&curtime);
	// Generate file name
	sPath = CUtils::FormatTime(curtime, m_sLogPath, m_pUser->GetTimezone());
	if (sPath.empty())
	{
		DEBUG("Could not format log path [" << sPath << "]");
		return;
	}

	// TODO: Properly handle IRC case mapping
	// $WINDOW has to be handled last, since it can contain %
	sPath.Replace("$USER", CString((m_pUser ? m_pUser->GetUserName() : "UNKNOWN")).AsLower());
	sPath.Replace("$NETWORK", CString((m_pNetwork ? m_pNetwork->GetName() : "znc")).AsLower());
	sPath.Replace("$WINDOW", CString(sWindow.Replace_n("/", "-").Replace_n("\\", "-")).AsLower());

	// Check if it's allowed to write in this specific path
	sPath = CDir::CheckPathPrefix(GetSavePath(), sPath);
	if (sPath.empty())
	{
		DEBUG("Invalid log path ["<<m_sLogPath<<"].");
		return;
	}

	CFile LogFile(sPath);
	CString sLogDir = LogFile.GetDir();
	struct stat ModDirInfo;
	CFile::GetInfo(GetSavePath(), ModDirInfo);
	if (!CFile::Exists(sLogDir)) CDir::MakeDir(sLogDir, ModDirInfo.st_mode);
	if (LogFile.Open(O_WRONLY | O_APPEND | O_CREAT))
	{
		LogFile.Write(CUtils::FormatTime(curtime, m_sTimestamp, m_pUser->GetTimezone()) + (m_bSanitize ? sLine.StripControls_n() : sLine) + "\n");
	} else
		DEBUG("Could not open log file [" << sPath << "]: " << strerror(errno));
}

void CLogMod::PutLog(const CString& sLine, const CChan& Channel)
{
	PutLog(sLine, Channel.GetName());
}

void CLogMod::PutLog(const CString& sLine, const CNick& Nick)
{
	PutLog(sLine, Nick.GetNick());
}

CString CLogMod::GetServer()
{
	CServer* pServer = m_pNetwork->GetCurrentServer();
	CString sSSL;

	if (!pServer)
		return "(no server)";

	if (pServer->IsSSL())
		sSSL = "+";
	return pServer->GetName() + " " + sSSL + CString(pServer->GetPort());
}

bool CLogMod::OnLoad(const CString& sArgs, CString& sMessage)
{
	VCString vsArgs;
	sArgs.Split(" ", vsArgs);

	bool bHaveTimestamp = false;
	bool bHaveLogPath = false;
	for (CString& sArg : vsArgs) {
		if (sArg.Equals("-sanitize")) {
			m_bSanitize = true;
		} else if (sArg.Equals("-timestamp")) {
			// Everything after this must be timestamp
			bHaveTimestamp = true;
		} else {
			if (bHaveTimestamp) {
				m_sTimestamp += sArg + " ";
			} else {
				// Only one arg may be LogPath
				if (bHaveLogPath) {
					sMessage = "Invalid args [" + sArgs + "]. Only one log path allowed.  Check that there are no spaces in the path.";
					return false;
				}
				m_sLogPath = sArg;
				bHaveLogPath = true;
			}
		}
	}

	if (m_sTimestamp.empty()) {
		m_sTimestamp = "[%H:%M:%S] ";
	}

	// Add default filename to path if it's a folder
	if (GetType() == CModInfo::UserModule) {
		if (m_sLogPath.Right(1) == "/" || m_sLogPath.find("$WINDOW") == CString::npos || m_sLogPath.find("$NETWORK") == CString::npos) {
			if (!m_sLogPath.empty()) {
				m_sLogPath += "/";
			}
			m_sLogPath += "$NETWORK/$WINDOW/%Y-%m-%d.log";
		}
	} else if (GetType() == CModInfo::NetworkModule) {
		if (m_sLogPath.Right(1) == "/" || m_sLogPath.find("$WINDOW") == CString::npos) {
			if (!m_sLogPath.empty()) {
				m_sLogPath += "/";
			}
			m_sLogPath += "$WINDOW/%Y-%m-%d.log";
		}
	} else {
		if (m_sLogPath.Right(1) == "/" || m_sLogPath.find("$USER") == CString::npos || m_sLogPath.find("$WINDOW") == CString::npos || m_sLogPath.find("$NETWORK") == CString::npos) {
			if (!m_sLogPath.empty()) {
				m_sLogPath += "/";
			}
			m_sLogPath += "$USER/$NETWORK/$WINDOW/%Y-%m-%d.log";
		}
	}

	// Check if it's allowed to write in this path in general
	m_sLogPath = CDir::CheckPathPrefix(GetSavePath(), m_sLogPath);
	if (m_sLogPath.empty())
	{
		sMessage = "Invalid log path ["+m_sLogPath+"].";
		return false;
	} else {
		sMessage = "Logging to ["+m_sLogPath+"]. Using timestamp ["+m_sTimestamp+"]";
		return true;
	}
}

void CLogMod::OnIRCConnected()
{
	PutLog("Connected to IRC (" + GetServer() + ")");
}

void CLogMod::OnIRCDisconnected()
{
	PutLog("Disconnected from IRC (" + GetServer() + ")");
}

CModule::EModRet CLogMod::OnBroadcast(CString& sMessage)
{
	PutLog("Broadcast: " + sMessage);
	return CONTINUE;
}

void CLogMod::OnRawMode2(const CNick* pOpNick, CChan& Channel, const CString& sModes, const CString& sArgs)
{
	const CString sNick = pOpNick ? pOpNick->GetNick() : "Server";
	PutLog("*** " + sNick + " sets mode: " + sModes + " " + sArgs, Channel);
}

void CLogMod::OnKick(const CNick& OpNick, const CString& sKickedNick, CChan& Channel, const CString& sMessage)
{
	PutLog("*** " + sKickedNick + " was kicked by " + OpNick.GetNick() + " (" + sMessage + ")", Channel);
}

void CLogMod::OnQuit(const CNick& Nick, const CString& sMessage, const vector<CChan*>& vChans)
{
	for (std::vector<CChan*>::const_iterator pChan = vChans.begin(); pChan != vChans.end(); ++pChan)
		PutLog("*** Quits: " + Nick.GetNick() + " (" + Nick.GetIdent() + "@" + Nick.GetHost() + ") (" + sMessage + ")", **pChan);
}

void CLogMod::OnJoin(const CNick& Nick, CChan& Channel)
{
	PutLog("*** Joins: " + Nick.GetNick() + " (" + Nick.GetIdent() + "@" + Nick.GetHost() + ")", Channel);
}

void CLogMod::OnPart(const CNick& Nick, CChan& Channel, const CString& sMessage)
{
	PutLog("*** Parts: " + Nick.GetNick() + " (" + Nick.GetIdent() + "@" + Nick.GetHost() + ") (" + sMessage + ")", Channel);
}

void CLogMod::OnNick(const CNick& OldNick, const CString& sNewNick, const vector<CChan*>& vChans)
{
	for (std::vector<CChan*>::const_iterator pChan = vChans.begin(); pChan != vChans.end(); ++pChan)
		PutLog("*** " + OldNick.GetNick() + " is now known as " + sNewNick, **pChan);
}

CModule::EModRet CLogMod::OnTopic(CNick& Nick, CChan& Channel, CString& sTopic)
{
	PutLog("*** " + Nick.GetNick() + " changes topic to '" + sTopic + "'", Channel);
	return CONTINUE;
}

/* notices */
CModule::EModRet CLogMod::OnUserNotice(CString& sTarget, CString& sMessage)
{
	if (m_pNetwork) {
		PutLog("-" + m_pNetwork->GetCurNick() + "- " + sMessage, sTarget);
	}

	return CONTINUE;
}

CModule::EModRet CLogMod::OnPrivNotice(CNick& Nick, CString& sMessage)
{
	PutLog("-" + Nick.GetNick() + "- " + sMessage, Nick);
	return CONTINUE;
}

CModule::EModRet CLogMod::OnChanNotice(CNick& Nick, CChan& Channel, CString& sMessage)
{
	PutLog("-" + Nick.GetNick() + "- " + sMessage, Channel);
	return CONTINUE;
}

/* actions */
CModule::EModRet CLogMod::OnUserAction(CString& sTarget, CString& sMessage)
{
	if (m_pNetwork) {
		PutLog("* " + m_pNetwork->GetCurNick() + " " + sMessage, sTarget);
	}

	return CONTINUE;
}

CModule::EModRet CLogMod::OnPrivAction(CNick& Nick, CString& sMessage)
{
	PutLog("* " + Nick.GetNick() + " " + sMessage, Nick);
	return CONTINUE;
}

CModule::EModRet CLogMod::OnChanAction(CNick& Nick, CChan& Channel, CString& sMessage)
{
	PutLog("* " + Nick.GetNick() + " " + sMessage, Channel);
	return CONTINUE;
}

/* msgs */
CModule::EModRet CLogMod::OnUserMsg(CString& sTarget, CString& sMessage)
{
	if (m_pNetwork) {
		PutLog("<" + m_pNetwork->GetCurNick() + "> " + sMessage, sTarget);
	}

	return CONTINUE;
}

CModule::EModRet CLogMod::OnPrivMsg(CNick& Nick, CString& sMessage)
{
	PutLog("<" + Nick.GetNick() + "> " + sMessage, Nick);
	return CONTINUE;
}

CModule::EModRet CLogMod::OnChanMsg(CNick& Nick, CChan& Channel, CString& sMessage)
{
	PutLog("<" + Nick.GetNick() + "> " + sMessage, Channel);
	return CONTINUE;
}

template<> void TModInfo<CLogMod>(CModInfo& Info) {
	Info.AddType(CModInfo::NetworkModule);
	Info.AddType(CModInfo::GlobalModule);
	Info.SetHasArgs(true);
	Info.SetArgsHelpText("[-sanitize] Optional path where to store logs.");
	Info.SetWikiPage("log");
}

USERMODULEDEFS(CLogMod, "Write IRC logs.")
<|endoftext|>


<commit_before>#include <v8.h>
#include <node.h>
#include <node_object_wrap.h>
#include <queue>

#include "lib/RtMidi/RtMidi.h"
#include "lib/RtMidi/RtMidi.cpp"

using namespace node;

#define SAFE_NODE_SET_PROTOTYPE_METHOD(templ, name, callback)             \
do {                                                                      \
  v8::Local<v8::Signature> __callback##_SIG = v8::Signature::New(templ);  \
  v8::Local<v8::FunctionTemplate> __callback##_TEM =                      \
    v8::FunctionTemplate::New(callback, v8::Handle<v8::Value>(),          \
                          __callback##_SIG);                              \
  templ->PrototypeTemplate()->Set(v8::String::NewSymbol(name),            \
                                  __callback##_TEM);                      \
} while (0)

class NodeMidiOutput : ObjectWrap
{
private:
    RtMidiOut* out;
public:
    static v8::Persistent<v8::FunctionTemplate> s_ct;
    static void Init(v8::Handle<v8::Object> target)
    {
        v8::HandleScope scope;

        v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);

        s_ct = v8::Persistent<v8::FunctionTemplate>::New(t);
        s_ct->InstanceTemplate()->SetInternalFieldCount(1);
        s_ct->SetClassName(v8::String::NewSymbol("NodeMidiOutput"));

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortCount", GetPortCount);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortName", GetPortName);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openPort", OpenPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openVirtualPort", OpenVirtualPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "closePort", ClosePort);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "sendMessage", SendMessage);

        target->Set(v8::String::NewSymbol("output"),
                    s_ct->GetFunction());
    }

    NodeMidiOutput()
    {
        out = new RtMidiOut();
    }

    ~NodeMidiOutput()
    {
        delete out;
    }

    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = new NodeMidiOutput();
        output->Wrap(args.This());
        return args.This();
    }

    static v8::Handle<v8::Value> GetPortCount(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        v8::Local<v8::Integer> result = v8::Uint32::New(output->out->getPortCount());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> GetPortName(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        v8::Local<v8::String> result = v8::String::New(output->out->getPortName(portNumber).c_str());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> OpenPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        output->out->openPort(portNumber);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> OpenVirtualPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsString()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be a string")));
        }
        std::string name(*v8::String::AsciiValue(args[0]));
        output->out->openVirtualPort(name);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> ClosePort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        output->out->closePort();
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> SendMessage(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsArray()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an array")));
        }
        v8::Local<v8::Object> message = args[0]->ToObject();
        size_t messageLength = message->Get(v8::String::New("length"))->Int32Value();
        std::vector<unsigned char> messageOutput;
        for (size_t i = 0; i != messageLength; ++i) {
            messageOutput.push_back(message->Get(v8::Integer::New(i))->Int32Value());
        }
        output->out->sendMessage(&messageOutput);
        return scope.Close(v8::Undefined());
    }
};

static v8::Persistent<v8::String> emit_symbol;

class NodeMidiInput : public ObjectWrap
{
private:
    RtMidiIn* in;

public:
    ev_async* message_async;
    pthread_mutex_t message_mutex;

    struct MidiMessage
    {
        double deltaTime;
        std::vector<unsigned char> message;
    };
    std::queue<MidiMessage*> message_queue;

    static v8::Persistent<v8::FunctionTemplate> s_ct;
    static void Init(v8::Handle<v8::Object> target)
    {
        v8::HandleScope scope;

        v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);

        s_ct = v8::Persistent<v8::FunctionTemplate>::New(t);
        s_ct->InstanceTemplate()->SetInternalFieldCount(1);

        emit_symbol = v8::Persistent<v8::String>::New(v8::String::NewSymbol("emit"));

        s_ct->SetClassName(v8::String::NewSymbol("NodeMidiInput"));

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortCount", GetPortCount);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortName", GetPortName);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openPort", OpenPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openVirtualPort", OpenVirtualPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "closePort", ClosePort);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "ignoreTypes", IgnoreTypes);

        target->Set(v8::String::NewSymbol("input"),
                    s_ct->GetFunction());
    }

    NodeMidiInput()
    {
        in = new RtMidiIn();
        pthread_mutex_init(&message_mutex, NULL);
        message_async = new ev_async();
    }

    ~NodeMidiInput()
    {
        in->closePort();
        ev_async_stop(EV_DEFAULT_UC_ message_async);
        delete message_async;
        delete in;
        pthread_mutex_destroy(&message_mutex);
    }

    static void EmitMessage(EV_P_ ev_async *w, int revents)
    {
        v8::HandleScope scope;
        NodeMidiInput *input = static_cast<NodeMidiInput*>(w->data);
        pthread_mutex_lock(&input->message_mutex);
        while (!input->message_queue.empty())
        {
            MidiMessage* message = input->message_queue.front();
            v8::Local<v8::Value> args[3];
            args[0]= v8::String::New("message");
            args[1] = v8::Local<v8::Value>::New(v8::Number::New(message->deltaTime));
            size_t count = message->message.size();
            v8::Local<v8::Array> data = v8::Array::New(count);
            for (size_t i = 0; i < count; ++i) {
                data->Set(v8::Number::New(i), v8::Integer::New(message->message[i]));
            }
            args[2] = v8::Local<v8::Value>::New(data);
            v8::Local<v8::Value> emit_v = input->handle_->Get(emit_symbol);
            if (emit_v->IsFunction()) {
                v8::Local<v8::Function> emit=v8::Local<v8::Function>::Cast(emit_v);
                v8::TryCatch tc;
                emit->Call(input->handle_,3,args);
                if (tc.HasCaught()){
                    node::FatalException(tc);
               }
            }
            input->message_queue.pop();
            delete message;
        }
        pthread_mutex_unlock(&input->message_mutex);
    }

    static void Callback(double deltaTime, std::vector<unsigned char> *message, void *userData)
    {
        NodeMidiInput *input = static_cast<NodeMidiInput*>(userData);
        MidiMessage* data = new MidiMessage();
        data->deltaTime = deltaTime;
        data->message = *message;
        pthread_mutex_lock(&input->message_mutex);
        input->message_queue.push(data);
        pthread_mutex_unlock(&input->message_mutex);
        ev_async_send(EV_DEFAULT_UC_ input->message_async);
    }

    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = new NodeMidiInput();
        input->message_async->data = input;
        ev_async_init(input->message_async, NodeMidiInput::EmitMessage);
        ev_async_start(EV_DEFAULT_UC_ input->message_async);
        ev_unref(EV_DEFAULT_UC);
        input->Wrap(args.This());
        return args.This();
    }

    static v8::Handle<v8::Value> GetPortCount(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        v8::Local<v8::Integer> result = v8::Uint32::New(input->in->getPortCount());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> GetPortName(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        v8::Local<v8::String> result = v8::String::New(input->in->getPortName(portNumber).c_str());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> OpenPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        input->Ref();
        input->in->setCallback(&NodeMidiInput::Callback, ObjectWrap::Unwrap<NodeMidiInput>(args.This()));
        input->in->openPort(portNumber);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> OpenVirtualPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsString()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be a string")));
        }
        std::string name(*v8::String::AsciiValue(args[0]));
        input->Ref();
        input->in->setCallback(&NodeMidiInput::Callback, ObjectWrap::Unwrap<NodeMidiInput>(args.This()));
        input->in->openVirtualPort(name);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> ClosePort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        input->Unref();
        input->in->closePort();
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> IgnoreTypes(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() != 3 || !args[0]->IsBoolean() || !args[1]->IsBoolean() || !args[2]->IsBoolean()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("Arguments must be boolean")));
        }
        int filter_sysex = args[0]->BooleanValue();
        int filter_timing = args[1]->BooleanValue();
        int filter_sensing = args[2]->BooleanValue();
        input->in->ignoreTypes(filter_sysex,filter_timing,filter_sensing);
        return scope.Close(v8::Undefined());
    }
};

v8::Persistent<v8::FunctionTemplate> NodeMidiOutput::s_ct;
v8::Persistent<v8::FunctionTemplate> NodeMidiInput::s_ct;

extern "C" {
    void init (v8::Handle<v8::Object> target)
    {
        NodeMidiOutput::Init(target);
        NodeMidiInput::Init(target);
    }

    NODE_MODULE(nodemidi, init);
}
<commit_msg>Tidy up some variable types in the the input IgnoreTypes function.<commit_after>#include <v8.h>
#include <node.h>
#include <node_object_wrap.h>
#include <queue>

#include "lib/RtMidi/RtMidi.h"
#include "lib/RtMidi/RtMidi.cpp"

using namespace node;

#define SAFE_NODE_SET_PROTOTYPE_METHOD(templ, name, callback)             \
do {                                                                      \
  v8::Local<v8::Signature> __callback##_SIG = v8::Signature::New(templ);  \
  v8::Local<v8::FunctionTemplate> __callback##_TEM =                      \
    v8::FunctionTemplate::New(callback, v8::Handle<v8::Value>(),          \
                          __callback##_SIG);                              \
  templ->PrototypeTemplate()->Set(v8::String::NewSymbol(name),            \
                                  __callback##_TEM);                      \
} while (0)

class NodeMidiOutput : ObjectWrap
{
private:
    RtMidiOut* out;
public:
    static v8::Persistent<v8::FunctionTemplate> s_ct;
    static void Init(v8::Handle<v8::Object> target)
    {
        v8::HandleScope scope;

        v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);

        s_ct = v8::Persistent<v8::FunctionTemplate>::New(t);
        s_ct->InstanceTemplate()->SetInternalFieldCount(1);
        s_ct->SetClassName(v8::String::NewSymbol("NodeMidiOutput"));

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortCount", GetPortCount);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortName", GetPortName);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openPort", OpenPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openVirtualPort", OpenVirtualPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "closePort", ClosePort);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "sendMessage", SendMessage);

        target->Set(v8::String::NewSymbol("output"),
                    s_ct->GetFunction());
    }

    NodeMidiOutput()
    {
        out = new RtMidiOut();
    }

    ~NodeMidiOutput()
    {
        delete out;
    }

    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = new NodeMidiOutput();
        output->Wrap(args.This());
        return args.This();
    }

    static v8::Handle<v8::Value> GetPortCount(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        v8::Local<v8::Integer> result = v8::Uint32::New(output->out->getPortCount());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> GetPortName(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        v8::Local<v8::String> result = v8::String::New(output->out->getPortName(portNumber).c_str());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> OpenPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        output->out->openPort(portNumber);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> OpenVirtualPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsString()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be a string")));
        }
        std::string name(*v8::String::AsciiValue(args[0]));
        output->out->openVirtualPort(name);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> ClosePort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        output->out->closePort();
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> SendMessage(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiOutput* output = ObjectWrap::Unwrap<NodeMidiOutput>(args.This());
        if (args.Length() == 0 || !args[0]->IsArray()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an array")));
        }
        v8::Local<v8::Object> message = args[0]->ToObject();
        size_t messageLength = message->Get(v8::String::New("length"))->Int32Value();
        std::vector<unsigned char> messageOutput;
        for (size_t i = 0; i != messageLength; ++i) {
            messageOutput.push_back(message->Get(v8::Integer::New(i))->Int32Value());
        }
        output->out->sendMessage(&messageOutput);
        return scope.Close(v8::Undefined());
    }
};

static v8::Persistent<v8::String> emit_symbol;

class NodeMidiInput : public ObjectWrap
{
private:
    RtMidiIn* in;

public:
    ev_async* message_async;
    pthread_mutex_t message_mutex;

    struct MidiMessage
    {
        double deltaTime;
        std::vector<unsigned char> message;
    };
    std::queue<MidiMessage*> message_queue;

    static v8::Persistent<v8::FunctionTemplate> s_ct;
    static void Init(v8::Handle<v8::Object> target)
    {
        v8::HandleScope scope;

        v8::Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(New);

        s_ct = v8::Persistent<v8::FunctionTemplate>::New(t);
        s_ct->InstanceTemplate()->SetInternalFieldCount(1);

        emit_symbol = v8::Persistent<v8::String>::New(v8::String::NewSymbol("emit"));

        s_ct->SetClassName(v8::String::NewSymbol("NodeMidiInput"));

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortCount", GetPortCount);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "getPortName", GetPortName);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openPort", OpenPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "openVirtualPort", OpenVirtualPort);
        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "closePort", ClosePort);

        SAFE_NODE_SET_PROTOTYPE_METHOD(s_ct, "ignoreTypes", IgnoreTypes);

        target->Set(v8::String::NewSymbol("input"),
                    s_ct->GetFunction());
    }

    NodeMidiInput()
    {
        in = new RtMidiIn();
        pthread_mutex_init(&message_mutex, NULL);
        message_async = new ev_async();
    }

    ~NodeMidiInput()
    {
        in->closePort();
        ev_async_stop(EV_DEFAULT_UC_ message_async);
        delete message_async;
        delete in;
        pthread_mutex_destroy(&message_mutex);
    }

    static void EmitMessage(EV_P_ ev_async *w, int revents)
    {
        v8::HandleScope scope;
        NodeMidiInput *input = static_cast<NodeMidiInput*>(w->data);
        pthread_mutex_lock(&input->message_mutex);
        while (!input->message_queue.empty())
        {
            MidiMessage* message = input->message_queue.front();
            v8::Local<v8::Value> args[3];
            args[0]= v8::String::New("message");
            args[1] = v8::Local<v8::Value>::New(v8::Number::New(message->deltaTime));
            size_t count = message->message.size();
            v8::Local<v8::Array> data = v8::Array::New(count);
            for (size_t i = 0; i < count; ++i) {
                data->Set(v8::Number::New(i), v8::Integer::New(message->message[i]));
            }
            args[2] = v8::Local<v8::Value>::New(data);
            v8::Local<v8::Value> emit_v = input->handle_->Get(emit_symbol);
            if (emit_v->IsFunction()) {
                v8::Local<v8::Function> emit=v8::Local<v8::Function>::Cast(emit_v);
                v8::TryCatch tc;
                emit->Call(input->handle_,3,args);
                if (tc.HasCaught()){
                    node::FatalException(tc);
               }
            }
            input->message_queue.pop();
            delete message;
        }
        pthread_mutex_unlock(&input->message_mutex);
    }

    static void Callback(double deltaTime, std::vector<unsigned char> *message, void *userData)
    {
        NodeMidiInput *input = static_cast<NodeMidiInput*>(userData);
        MidiMessage* data = new MidiMessage();
        data->deltaTime = deltaTime;
        data->message = *message;
        pthread_mutex_lock(&input->message_mutex);
        input->message_queue.push(data);
        pthread_mutex_unlock(&input->message_mutex);
        ev_async_send(EV_DEFAULT_UC_ input->message_async);
    }

    static v8::Handle<v8::Value> New(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = new NodeMidiInput();
        input->message_async->data = input;
        ev_async_init(input->message_async, NodeMidiInput::EmitMessage);
        ev_async_start(EV_DEFAULT_UC_ input->message_async);
        ev_unref(EV_DEFAULT_UC);
        input->Wrap(args.This());
        return args.This();
    }

    static v8::Handle<v8::Value> GetPortCount(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        v8::Local<v8::Integer> result = v8::Uint32::New(input->in->getPortCount());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> GetPortName(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        v8::Local<v8::String> result = v8::String::New(input->in->getPortName(portNumber).c_str());
        return scope.Close(result);
    }

    static v8::Handle<v8::Value> OpenPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsUint32()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be an integer")));
        }
        unsigned int portNumber = args[0]->Uint32Value();
        input->Ref();
        input->in->setCallback(&NodeMidiInput::Callback, ObjectWrap::Unwrap<NodeMidiInput>(args.This()));
        input->in->openPort(portNumber);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> OpenVirtualPort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() == 0 || !args[0]->IsString()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("First argument must be a string")));
        }
        std::string name(*v8::String::AsciiValue(args[0]));
        input->Ref();
        input->in->setCallback(&NodeMidiInput::Callback, ObjectWrap::Unwrap<NodeMidiInput>(args.This()));
        input->in->openVirtualPort(name);
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> ClosePort(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        input->Unref();
        input->in->closePort();
        return scope.Close(v8::Undefined());
    }

    static v8::Handle<v8::Value> IgnoreTypes(const v8::Arguments& args)
    {
        v8::HandleScope scope;
        NodeMidiInput* input = ObjectWrap::Unwrap<NodeMidiInput>(args.This());
        if (args.Length() != 3 || !args[0]->IsBoolean() || !args[1]->IsBoolean() || !args[2]->IsBoolean()) {
            return ThrowException(v8::Exception::TypeError(
                v8::String::New("Arguments must be boolean")));
        }
        bool filter_sysex = args[0]->BooleanValue();
        bool filter_timing = args[1]->BooleanValue();
        bool filter_sensing = args[2]->BooleanValue();
        input->in->ignoreTypes(filter_sysex, filter_timing, filter_sensing);
        return scope.Close(v8::Undefined());
    }
};

v8::Persistent<v8::FunctionTemplate> NodeMidiOutput::s_ct;
v8::Persistent<v8::FunctionTemplate> NodeMidiInput::s_ct;

extern "C" {
    void init (v8::Handle<v8::Object> target)
    {
        NodeMidiOutput::Init(target);
        NodeMidiInput::Init(target);
    }

    NODE_MODULE(nodemidi, init);
}
<|endoftext|>


<commit_before>#include <assert.h>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <sstream>

#include "yaml-cpp/exceptions.h"
#include "yaml-cpp/node/detail/memory.h"
#include "yaml-cpp/node/detail/node.h"  // IWYU pragma: keep
#include "yaml-cpp/node/detail/node_data.h"
#include "yaml-cpp/node/detail/node_iterator.h"
#include "yaml-cpp/node/ptr.h"
#include "yaml-cpp/node/type.h"

namespace YAML {
namespace detail {

std::string node_data::empty_scalar;

node_data::node_data()
    : m_isDefined(false),
      m_type(NodeType::Null),
      m_seqSize(0),
      m_style(EmitterStyle::Default) {}

void node_data::mark_defined() {
  if (m_type == NodeType::Undefined)
    m_type = NodeType::Null;
  m_isDefined = true;
}

void node_data::set_type(NodeType::value type) {
  if (type == NodeType::Undefined) {
    m_type = type;
    m_isDefined = false;
    return;
  }

  m_isDefined = true;
  if (type == m_type)
    return;

  m_type = type;

  switch (m_type) {
    case NodeType::Null:
      break;
    case NodeType::Scalar:
      m_scalar.clear();
      break;
    case NodeType::Sequence:
      reset_sequence();
      break;
    case NodeType::Map:
      reset_map();
      break;
    case NodeType::Undefined:
      assert(false);
      break;
  }
}

void node_data::set_tag(const std::string& tag) { m_tag = tag; }

void node_data::set_style(EmitterStyle::value style) { m_style = style; }

void node_data::set_null() {
  m_isDefined = true;
  m_type = NodeType::Null;
}

void node_data::set_scalar(const std::string& scalar) {
  m_isDefined = true;
  m_type = NodeType::Scalar;
  m_scalar = scalar;
}

// size/iterator
std::size_t node_data::size() const {
  if (!m_isDefined)
    return 0;

  switch (m_type) {
    case NodeType::Sequence:
      compute_seq_size();
      return m_seqSize;
    case NodeType::Map:
      compute_map_size();
      return m_map.size() - m_undefinedPairs.size();
    default:
      return 0;
  }
  return 0;
}

void node_data::compute_seq_size() const {
  while (m_seqSize < m_sequence.size() && m_sequence[m_seqSize]->is_defined())
    m_seqSize++;
}

void node_data::compute_map_size() const {
  kv_pairs::iterator it = m_undefinedPairs.begin();
  while (it != m_undefinedPairs.end()) {
    kv_pairs::iterator jt = boost::next(it);
    if (it->first->is_defined() && it->second->is_defined())
      m_undefinedPairs.erase(it);
    it = jt;
  }
}

const_node_iterator node_data::begin() const {
  if (!m_isDefined)
    return const_node_iterator();

  switch (m_type) {
    case NodeType::Sequence:
      return const_node_iterator(m_sequence.begin());
    case NodeType::Map:
      return const_node_iterator(m_map.begin(), m_map.end());
    default:
      return const_node_iterator();
  }
}

node_iterator node_data::begin() {
  if (!m_isDefined)
    return node_iterator();

  switch (m_type) {
    case NodeType::Sequence:
      return node_iterator(m_sequence.begin());
    case NodeType::Map:
      return node_iterator(m_map.begin(), m_map.end());
    default:
      return node_iterator();
  }
}

const_node_iterator node_data::end() const {
  if (!m_isDefined)
    return const_node_iterator();

  switch (m_type) {
    case NodeType::Sequence:
      return const_node_iterator(m_sequence.end());
    case NodeType::Map:
      return const_node_iterator(m_map.end(), m_map.end());
    default:
      return const_node_iterator();
  }
}

node_iterator node_data::end() {
  if (!m_isDefined)
    return node_iterator();

  switch (m_type) {
    case NodeType::Sequence:
      return node_iterator(m_sequence.end());
    case NodeType::Map:
      return node_iterator(m_map.end(), m_map.end());
    default:
      return node_iterator();
  }
}

// sequence
void node_data::push_back(node& node, shared_memory_holder /* pMemory */) {
  if (m_type == NodeType::Undefined || m_type == NodeType::Null) {
    m_type = NodeType::Sequence;
    reset_sequence();
  }

  if (m_type != NodeType::Sequence)
    throw BadPushback();

  m_sequence.push_back(&node);
}

void node_data::insert(node& key, node& value, shared_memory_holder pMemory) {
  switch (m_type) {
    case NodeType::Map:
      break;
    case NodeType::Undefined:
    case NodeType::Null:
    case NodeType::Sequence:
      convert_to_map(pMemory);
      break;
    case NodeType::Scalar:
      throw BadSubscript();
  }

  insert_map_pair(key, value);
}

// indexing
node& node_data::get(node& key, shared_memory_holder pMemory) const {
  if (m_type != NodeType::Map)
    return pMemory->create_node();

  for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
    if (it->first->is(key))
      return *it->second;
  }

  return pMemory->create_node();
}

node& node_data::get(node& key, shared_memory_holder pMemory) {
  switch (m_type) {
    case NodeType::Map:
      break;
    case NodeType::Undefined:
    case NodeType::Null:
    case NodeType::Sequence:
      convert_to_map(pMemory);
      break;
    case NodeType::Scalar:
      throw BadSubscript();
  }

  for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
    if (it->first->is(key))
      return *it->second;
  }

  node& value = pMemory->create_node();
  insert_map_pair(key, value);
  return value;
}

bool node_data::remove(node& key, shared_memory_holder /* pMemory */) {
  if (m_type != NodeType::Map)
    return false;

  for (node_map::iterator it = m_map.begin(); it != m_map.end(); ++it) {
    if (it->first->is(key)) {
      m_map.erase(it);
      return true;
    }
  }

  return false;
}

void node_data::reset_sequence() {
  m_sequence.clear();
  m_seqSize = 0;
}

void node_data::reset_map() {
  m_map.clear();
  m_undefinedPairs.clear();
}

void node_data::insert_map_pair(node& key, node& value) {
  m_map[&key] = &value;
  if (!key.is_defined() || !value.is_defined())
    m_undefinedPairs.push_back(kv_pair(&key, &value));
}

void node_data::convert_to_map(shared_memory_holder pMemory) {
  switch (m_type) {
    case NodeType::Undefined:
    case NodeType::Null:
      reset_map();
      m_type = NodeType::Map;
      break;
    case NodeType::Sequence:
      convert_sequence_to_map(pMemory);
      break;
    case NodeType::Map:
      break;
    case NodeType::Scalar:
      assert(false);
      break;
  }
}

void node_data::convert_sequence_to_map(shared_memory_holder pMemory) {
  assert(m_type == NodeType::Sequence);

  reset_map();
  for (std::size_t i = 0; i < m_sequence.size(); i++) {
    std::stringstream stream;
    stream << i;

    node& key = pMemory->create_node();
    key.set_scalar(stream.str());
    insert_map_pair(key, *m_sequence[i]);
  }

  reset_sequence();
  m_type = NodeType::Map;
}
}
}
<commit_msg>Fix initialize ordering warning<commit_after>#include <assert.h>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <sstream>

#include "yaml-cpp/exceptions.h"
#include "yaml-cpp/node/detail/memory.h"
#include "yaml-cpp/node/detail/node.h"  // IWYU pragma: keep
#include "yaml-cpp/node/detail/node_data.h"
#include "yaml-cpp/node/detail/node_iterator.h"
#include "yaml-cpp/node/ptr.h"
#include "yaml-cpp/node/type.h"

namespace YAML {
namespace detail {

std::string node_data::empty_scalar;

node_data::node_data()
    : m_isDefined(false),
      m_type(NodeType::Null),
      m_style(EmitterStyle::Default),
      m_seqSize(0) {}

void node_data::mark_defined() {
  if (m_type == NodeType::Undefined)
    m_type = NodeType::Null;
  m_isDefined = true;
}

void node_data::set_type(NodeType::value type) {
  if (type == NodeType::Undefined) {
    m_type = type;
    m_isDefined = false;
    return;
  }

  m_isDefined = true;
  if (type == m_type)
    return;

  m_type = type;

  switch (m_type) {
    case NodeType::Null:
      break;
    case NodeType::Scalar:
      m_scalar.clear();
      break;
    case NodeType::Sequence:
      reset_sequence();
      break;
    case NodeType::Map:
      reset_map();
      break;
    case NodeType::Undefined:
      assert(false);
      break;
  }
}

void node_data::set_tag(const std::string& tag) { m_tag = tag; }

void node_data::set_style(EmitterStyle::value style) { m_style = style; }

void node_data::set_null() {
  m_isDefined = true;
  m_type = NodeType::Null;
}

void node_data::set_scalar(const std::string& scalar) {
  m_isDefined = true;
  m_type = NodeType::Scalar;
  m_scalar = scalar;
}

// size/iterator
std::size_t node_data::size() const {
  if (!m_isDefined)
    return 0;

  switch (m_type) {
    case NodeType::Sequence:
      compute_seq_size();
      return m_seqSize;
    case NodeType::Map:
      compute_map_size();
      return m_map.size() - m_undefinedPairs.size();
    default:
      return 0;
  }
  return 0;
}

void node_data::compute_seq_size() const {
  while (m_seqSize < m_sequence.size() && m_sequence[m_seqSize]->is_defined())
    m_seqSize++;
}

void node_data::compute_map_size() const {
  kv_pairs::iterator it = m_undefinedPairs.begin();
  while (it != m_undefinedPairs.end()) {
    kv_pairs::iterator jt = boost::next(it);
    if (it->first->is_defined() && it->second->is_defined())
      m_undefinedPairs.erase(it);
    it = jt;
  }
}

const_node_iterator node_data::begin() const {
  if (!m_isDefined)
    return const_node_iterator();

  switch (m_type) {
    case NodeType::Sequence:
      return const_node_iterator(m_sequence.begin());
    case NodeType::Map:
      return const_node_iterator(m_map.begin(), m_map.end());
    default:
      return const_node_iterator();
  }
}

node_iterator node_data::begin() {
  if (!m_isDefined)
    return node_iterator();

  switch (m_type) {
    case NodeType::Sequence:
      return node_iterator(m_sequence.begin());
    case NodeType::Map:
      return node_iterator(m_map.begin(), m_map.end());
    default:
      return node_iterator();
  }
}

const_node_iterator node_data::end() const {
  if (!m_isDefined)
    return const_node_iterator();

  switch (m_type) {
    case NodeType::Sequence:
      return const_node_iterator(m_sequence.end());
    case NodeType::Map:
      return const_node_iterator(m_map.end(), m_map.end());
    default:
      return const_node_iterator();
  }
}

node_iterator node_data::end() {
  if (!m_isDefined)
    return node_iterator();

  switch (m_type) {
    case NodeType::Sequence:
      return node_iterator(m_sequence.end());
    case NodeType::Map:
      return node_iterator(m_map.end(), m_map.end());
    default:
      return node_iterator();
  }
}

// sequence
void node_data::push_back(node& node, shared_memory_holder /* pMemory */) {
  if (m_type == NodeType::Undefined || m_type == NodeType::Null) {
    m_type = NodeType::Sequence;
    reset_sequence();
  }

  if (m_type != NodeType::Sequence)
    throw BadPushback();

  m_sequence.push_back(&node);
}

void node_data::insert(node& key, node& value, shared_memory_holder pMemory) {
  switch (m_type) {
    case NodeType::Map:
      break;
    case NodeType::Undefined:
    case NodeType::Null:
    case NodeType::Sequence:
      convert_to_map(pMemory);
      break;
    case NodeType::Scalar:
      throw BadSubscript();
  }

  insert_map_pair(key, value);
}

// indexing
node& node_data::get(node& key, shared_memory_holder pMemory) const {
  if (m_type != NodeType::Map)
    return pMemory->create_node();

  for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
    if (it->first->is(key))
      return *it->second;
  }

  return pMemory->create_node();
}

node& node_data::get(node& key, shared_memory_holder pMemory) {
  switch (m_type) {
    case NodeType::Map:
      break;
    case NodeType::Undefined:
    case NodeType::Null:
    case NodeType::Sequence:
      convert_to_map(pMemory);
      break;
    case NodeType::Scalar:
      throw BadSubscript();
  }

  for (node_map::const_iterator it = m_map.begin(); it != m_map.end(); ++it) {
    if (it->first->is(key))
      return *it->second;
  }

  node& value = pMemory->create_node();
  insert_map_pair(key, value);
  return value;
}

bool node_data::remove(node& key, shared_memory_holder /* pMemory */) {
  if (m_type != NodeType::Map)
    return false;

  for (node_map::iterator it = m_map.begin(); it != m_map.end(); ++it) {
    if (it->first->is(key)) {
      m_map.erase(it);
      return true;
    }
  }

  return false;
}

void node_data::reset_sequence() {
  m_sequence.clear();
  m_seqSize = 0;
}

void node_data::reset_map() {
  m_map.clear();
  m_undefinedPairs.clear();
}

void node_data::insert_map_pair(node& key, node& value) {
  m_map[&key] = &value;
  if (!key.is_defined() || !value.is_defined())
    m_undefinedPairs.push_back(kv_pair(&key, &value));
}

void node_data::convert_to_map(shared_memory_holder pMemory) {
  switch (m_type) {
    case NodeType::Undefined:
    case NodeType::Null:
      reset_map();
      m_type = NodeType::Map;
      break;
    case NodeType::Sequence:
      convert_sequence_to_map(pMemory);
      break;
    case NodeType::Map:
      break;
    case NodeType::Scalar:
      assert(false);
      break;
  }
}

void node_data::convert_sequence_to_map(shared_memory_holder pMemory) {
  assert(m_type == NodeType::Sequence);

  reset_map();
  for (std::size_t i = 0; i < m_sequence.size(); i++) {
    std::stringstream stream;
    stream << i;

    node& key = pMemory->create_node();
    key.set_scalar(stream.str());
    insert_map_pair(key, *m_sequence[i]);
  }

  reset_sequence();
  m_type = NodeType::Map;
}
}
}
<|endoftext|>


<commit_before>#include <node.h>
#include <nan.h>
#include <secp256k1.h>
#include <scalar_impl.h>
#include <lax_der_privatekey_parsing.h>

#include "messages.h"
#include "util.h"

extern secp256k1_context* secp256k1ctx;

NAN_METHOD(privateKeyVerify) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  const unsigned char* private_key = (const unsigned char*) node::Buffer::Data(private_key_buffer);

  if (node::Buffer::Length(private_key_buffer) != 32) {
    return info.GetReturnValue().Set(Nan::New<v8::Boolean>(false));
  }

  int result = secp256k1_ec_seckey_verify(secp256k1ctx, private_key);
  info.GetReturnValue().Set(Nan::New<v8::Boolean>(result));
}

NAN_METHOD(privateKeyExport) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(private_key_buffer, 32, EC_PRIVATE_KEY_LENGTH_INVALID);
  const unsigned char* private_key = (const unsigned char*) node::Buffer::Data(private_key_buffer);

  int compressed = 1;
  UPDATE_COMPRESSED_VALUE(compressed, info[1], 1, 0);

  unsigned char output[279];
  size_t output_length;
  if (ec_privkey_export_der(secp256k1ctx, &output[0], &output_length, private_key, compressed) == 0) {
    return Nan::ThrowError(EC_PRIVATE_KEY_EXPORT_DER_FAIL);
  }

  info.GetReturnValue().Set(COPY_BUFFER(output, output_length));
}

NAN_METHOD(privateKeyImport) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> input_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(input_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH_GT_ZERO(input_buffer, EC_PRIVATE_KEY_LENGTH_INVALID);
  const unsigned char* input = (const unsigned char*) node::Buffer::Data(input_buffer);
  size_t input_length = node::Buffer::Length(input_buffer);

  unsigned char private_key[32];
  if (ec_privkey_import_der(secp256k1ctx, &private_key[0], input, input_length) == 0) {
    return Nan::ThrowError(EC_PRIVATE_KEY_IMPORT_DER_FAIL);
  }

  info.GetReturnValue().Set(COPY_BUFFER(private_key, 32));
}

NAN_METHOD(privateKeyNegate) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(private_key_buffer, 32, EC_PRIVATE_KEY_LENGTH_INVALID);
  unsigned char private_key[32];
  memcpy(&private_key[0], node::Buffer::Data(private_key_buffer), 32);

  secp256k1_ec_privkey_negate(secp256k1ctx, &private_key[0]);

  info.GetReturnValue().Set(COPY_BUFFER(&private_key[0], 32));
}

NAN_METHOD(privateKeyModInverse) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(private_key_buffer, 32, EC_PRIVATE_KEY_LENGTH_INVALID);
  const unsigned char* private_key = (const unsigned char*) node::Buffer::Data(private_key_buffer);

  secp256k1_scalar s;
  int overflow = 0;
  secp256k1_scalar_set_b32(&s, private_key, &overflow);
  if (overflow || secp256k1_scalar_is_zero(&s)) {
    secp256k1_scalar_clear(&s);
    return Nan::ThrowError(EC_PRIVATE_KEY_RANGE_INVALID);
  }

  secp256k1_scalar_inverse(&s, &s);

  unsigned char b[32];
  secp256k1_scalar_get_b32(b, &s);
  secp256k1_scalar_clear(&s);

  info.GetReturnValue().Set(COPY_BUFFER(&b[0], 32));
}

NAN_METHOD(privateKeyTweakAdd) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(private_key_buffer, 32, EC_PRIVATE_KEY_LENGTH_INVALID);
  unsigned char private_key[32];
  memcpy(&private_key[0], node::Buffer::Data(private_key_buffer), 32);

  v8::Local<v8::Object> tweak_buffer = info[1].As<v8::Object>();
  CHECK_TYPE_BUFFER(tweak_buffer, TWEAK_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(tweak_buffer, 32, TWEAK_LENGTH_INVALID);
  const unsigned char* tweak = (unsigned char *) node::Buffer::Data(tweak_buffer);

  if (secp256k1_ec_privkey_tweak_add(secp256k1ctx, &private_key[0], tweak) == 0) {
    return Nan::ThrowError(EC_PRIVATE_KEY_TWEAK_ADD_FAIL);
  }

  info.GetReturnValue().Set(COPY_BUFFER(&private_key[0], 32));
}

NAN_METHOD(privateKeyTweakMul) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(private_key_buffer, 32, EC_PRIVATE_KEY_LENGTH_INVALID);
  unsigned char private_key[32];
  memcpy(&private_key[0], node::Buffer::Data(private_key_buffer), 32);

  v8::Local<v8::Object> tweak_buffer = info[1].As<v8::Object>();
  CHECK_TYPE_BUFFER(tweak_buffer, TWEAK_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(tweak_buffer, 32, TWEAK_LENGTH_INVALID);
  const unsigned char* tweak = (unsigned char *) node::Buffer::Data(tweak_buffer);

  if (secp256k1_ec_privkey_tweak_mul(secp256k1ctx, &private_key[0], tweak) == 0) {
    return Nan::ThrowError(EC_PRIVATE_KEY_TWEAK_MUL_FAIL);
  }

  info.GetReturnValue().Set(COPY_BUFFER(&private_key[0], 32));
}
<commit_msg>Improve memory usage of privateKeyModInverse<commit_after>#include <node.h>
#include <nan.h>
#include <secp256k1.h>
#include <scalar_impl.h>
#include <lax_der_privatekey_parsing.h>

#include "messages.h"
#include "util.h"

extern secp256k1_context* secp256k1ctx;

NAN_METHOD(privateKeyVerify) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  const unsigned char* private_key = (const unsigned char*) node::Buffer::Data(private_key_buffer);

  if (node::Buffer::Length(private_key_buffer) != 32) {
    return info.GetReturnValue().Set(Nan::New<v8::Boolean>(false));
  }

  int result = secp256k1_ec_seckey_verify(secp256k1ctx, private_key);
  info.GetReturnValue().Set(Nan::New<v8::Boolean>(result));
}

NAN_METHOD(privateKeyExport) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(private_key_buffer, 32, EC_PRIVATE_KEY_LENGTH_INVALID);
  const unsigned char* private_key = (const unsigned char*) node::Buffer::Data(private_key_buffer);

  int compressed = 1;
  UPDATE_COMPRESSED_VALUE(compressed, info[1], 1, 0);

  unsigned char output[279];
  size_t output_length;
  if (ec_privkey_export_der(secp256k1ctx, &output[0], &output_length, private_key, compressed) == 0) {
    return Nan::ThrowError(EC_PRIVATE_KEY_EXPORT_DER_FAIL);
  }

  info.GetReturnValue().Set(COPY_BUFFER(output, output_length));
}

NAN_METHOD(privateKeyImport) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> input_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(input_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH_GT_ZERO(input_buffer, EC_PRIVATE_KEY_LENGTH_INVALID);
  const unsigned char* input = (const unsigned char*) node::Buffer::Data(input_buffer);
  size_t input_length = node::Buffer::Length(input_buffer);

  unsigned char private_key[32];
  if (ec_privkey_import_der(secp256k1ctx, &private_key[0], input, input_length) == 0) {
    return Nan::ThrowError(EC_PRIVATE_KEY_IMPORT_DER_FAIL);
  }

  info.GetReturnValue().Set(COPY_BUFFER(private_key, 32));
}

NAN_METHOD(privateKeyNegate) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(private_key_buffer, 32, EC_PRIVATE_KEY_LENGTH_INVALID);
  unsigned char private_key[32];
  memcpy(&private_key[0], node::Buffer::Data(private_key_buffer), 32);

  secp256k1_ec_privkey_negate(secp256k1ctx, &private_key[0]);

  info.GetReturnValue().Set(COPY_BUFFER(&private_key[0], 32));
}

NAN_METHOD(privateKeyModInverse) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(private_key_buffer, 32, EC_PRIVATE_KEY_LENGTH_INVALID);
  unsigned char private_key[32];
  memcpy(&private_key[0], node::Buffer::Data(private_key_buffer), 32);

  secp256k1_scalar s;
  int overflow = 0;
  secp256k1_scalar_set_b32(&s, private_key, &overflow);
  if (overflow || secp256k1_scalar_is_zero(&s)) {
    secp256k1_scalar_clear(&s);
    return Nan::ThrowError(EC_PRIVATE_KEY_RANGE_INVALID);
  }

  secp256k1_scalar_inverse(&s, &s);

  secp256k1_scalar_get_b32(private_key, &s);
  secp256k1_scalar_clear(&s);

  info.GetReturnValue().Set(COPY_BUFFER(&private_key[0], 32));
}

NAN_METHOD(privateKeyTweakAdd) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(private_key_buffer, 32, EC_PRIVATE_KEY_LENGTH_INVALID);
  unsigned char private_key[32];
  memcpy(&private_key[0], node::Buffer::Data(private_key_buffer), 32);

  v8::Local<v8::Object> tweak_buffer = info[1].As<v8::Object>();
  CHECK_TYPE_BUFFER(tweak_buffer, TWEAK_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(tweak_buffer, 32, TWEAK_LENGTH_INVALID);
  const unsigned char* tweak = (unsigned char *) node::Buffer::Data(tweak_buffer);

  if (secp256k1_ec_privkey_tweak_add(secp256k1ctx, &private_key[0], tweak) == 0) {
    return Nan::ThrowError(EC_PRIVATE_KEY_TWEAK_ADD_FAIL);
  }

  info.GetReturnValue().Set(COPY_BUFFER(&private_key[0], 32));
}

NAN_METHOD(privateKeyTweakMul) {
  Nan::HandleScope scope;

  v8::Local<v8::Object> private_key_buffer = info[0].As<v8::Object>();
  CHECK_TYPE_BUFFER(private_key_buffer, EC_PRIVATE_KEY_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(private_key_buffer, 32, EC_PRIVATE_KEY_LENGTH_INVALID);
  unsigned char private_key[32];
  memcpy(&private_key[0], node::Buffer::Data(private_key_buffer), 32);

  v8::Local<v8::Object> tweak_buffer = info[1].As<v8::Object>();
  CHECK_TYPE_BUFFER(tweak_buffer, TWEAK_TYPE_INVALID);
  CHECK_BUFFER_LENGTH(tweak_buffer, 32, TWEAK_LENGTH_INVALID);
  const unsigned char* tweak = (unsigned char *) node::Buffer::Data(tweak_buffer);

  if (secp256k1_ec_privkey_tweak_mul(secp256k1ctx, &private_key[0], tweak) == 0) {
    return Nan::ThrowError(EC_PRIVATE_KEY_TWEAK_MUL_FAIL);
  }

  info.GetReturnValue().Set(COPY_BUFFER(&private_key[0], 32));
}
<|endoftext|>