<commit_before>#include "library.h"
#include <algorithm>
#include "dirent.h"
#include <tchar.h>
#include <sstream>
#include <iterator>
#include <set>
#include <string>
#include <cctype>
#include "Shlwapi.h"
#include <regex>

//---------------------------------------------------------------------------------------------------
// tokenSplit
//---------------------------------------------------------------------------------------------------
std::vector<std::wstring> tokenSplit(const std::wstring& str, const std::wstring& delim)
{
	std::vector<std::wstring> tokens;

	std::wstring::size_type p0 = str.find_first_not_of(delim, 0);
	std::wstring::size_type p1 = str.find_first_of(delim, p0);

	while (p0 != p1 && p0 != std::wstring::npos)
	{
		tokens.push_back(str.substr(p0, p1 - p0));

		p0 = str.find_first_not_of(delim, p1);
		p1 = str.find_first_of(delim, p0);
	}

	return tokens;
}

//---------------------------------------------------------------------------------------------------
// tokenMerge
//---------------------------------------------------------------------------------------------------
std::wstring tokenMerge(const std::vector<std::wstring>& tokens, const std::wstring& delim)
{
	std::wstring tokenString;

	for (std::vector<std::wstring>::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
	{
		if (itr == tokens.begin())
		{
			tokenString += *itr;
		}
		else
		{
			tokenString += delim + *itr;
		}
	}

	return tokenString;
}

//---------------------------------------------------------------------------------------------------
// toLower
//---------------------------------------------------------------------------------------------------
std::wstring toLower(const std::wstring& str)
{
	std::wstring temp = str;
	std::transform(str.begin(), str.end(), temp.begin(), ::tolower);
	//std::transform(str.begin(), str.end(), temp.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c); });

	return temp;
}

//---------------------------------------------------------------------------------------------------
// hashString
// FNV www.isthe.com/chongo/tech/comp/fnv/
//---------------------------------------------------------------------------------------------------
unsigned int hashString(const std::wstring& str)
{
	static const unsigned int magic = 0x01000193;

	unsigned char *s = (unsigned char *)str.c_str();

	unsigned int hval = 0;

	unsigned int count = str.size() * sizeof(TCHAR);

	for (int i=0; i!=count; ++i)
	{
		hval *= magic;
		hval ^= *s++;
	}

	return hval;
}

//---------------------------------------------------------------------------------------------------
// Library
//---------------------------------------------------------------------------------------------------
Library::Library(const std::wstring& savePath)
{
	m_SavePath = savePath;
	m_SavePath += L"\\";
	m_SavePath += L"VGPLibrarian.cfg";
}

//---------------------------------------------------------------------------------------------------
// AddScanPath
//---------------------------------------------------------------------------------------------------
void Library::AddScanPath(const std::wstring& rootPath, bool runScan)
{
	std::wstring path = toLower(rootPath);

	if (std::find(m_ScanPaths.begin(), m_ScanPaths.end(), path) == m_ScanPaths.end())
	{
		m_ScanPaths.push_back(path);
	}

	if (runScan)
	{
		ScanFolder(path);
	}
}

//---------------------------------------------------------------------------------------------------
// Scan
//---------------------------------------------------------------------------------------------------
void Library::Scan()
{
	for (PathArray::iterator itr = m_ScanPaths.begin(); itr != m_ScanPaths.end(); ++itr)
	{
		ScanFolder((*itr).c_str());
	}
}

//---------------------------------------------------------------------------------------------------
// Clear
//---------------------------------------------------------------------------------------------------
void Library::Clear()
{
	for (DocumentList::const_iterator itr = m_Documents.GetDocuments().begin(); itr != m_Documents.GetDocuments().end(); ++itr)
	{
		delete (*itr);
	}

	m_Documents.Clear();
}

//---------------------------------------------------------------------------------------------------
// ScanFolder
//---------------------------------------------------------------------------------------------------
void Library::ScanFolder(const std::wstring& path)
{
    _WDIR* dir = _wopendir(path.c_str());

	if (!dir)
		return;

	struct _wdirent* dirEntry;

	while ((dirEntry = _wreaddir(dir)) != NULL)
	{
		std::wstring fullPath = path + _T("\\") + dirEntry->d_name;

		switch (dirEntry->d_type)
		{
			case DT_REG:
			{
				AddDocument(fullPath, L"", false);
				break;
			}

			case DT_DIR:
				if (wcscmp(dirEntry->d_name, L".") != 0 && wcscmp(dirEntry->d_name, L"..") != 0)
				{
					ScanFolder(fullPath);
				}
			break;
		}
	}

	wclosedir(dir);
}

//---------------------------------------------------------------------------------------------------
// LoadMeta
//---------------------------------------------------------------------------------------------------
void Library::LoadMeta()
{
	FILE* hMetaFile;
	_wfopen_s(&hMetaFile, m_SavePath.c_str(), L"r,ccs=UTF-8");

	if (!hMetaFile)
		return;

	WCHAR buffer[1024];
	while (fgetws(buffer, 1024, hMetaFile))
	{
		std::wstring line = buffer;

		std::wstring::size_type quote1 = line.find(L"\"", 0);
		std::wstring::size_type quote2 = line.find(L"\"", quote1+1);
		std::wstring::size_type comma1 = line.find(L",", quote2 + 1);
		std::wstring::size_type newline = line.find(L"\n");

		std::wstring path = line.substr(quote1 + 1, quote2 - quote1 - 1);
		std::wstring keywords = line.substr(comma1 + 1, newline - comma1 - 1);

		AddDocument(path, keywords, false);
	}

	fclose(hMetaFile);
}

//---------------------------------------------------------------------------------------------------
// SaveMeta
//---------------------------------------------------------------------------------------------------
void Library::SaveMeta() const
{
	FILE* hFile;
	_wfopen_s(&hFile, m_SavePath.c_str(), L"w,ccs=UTF-8");

	if (!hFile)
		return;

	for (PathArray::const_iterator itr = m_ScanPaths.begin(); itr != m_ScanPaths.end(); ++itr)
	{
		fwprintf(hFile, L"\"%s\"\n", (*itr).c_str());
	}

	for (DocumentList::const_iterator itr = m_Documents.GetDocuments().begin(); itr != m_Documents.GetDocuments().end(); ++itr)
	{
		fwprintf(hFile, L"\"%s\",%s\n", (*itr)->m_path.c_str(), (*itr)->m_keywords.c_str());
	}

	fclose(hFile);
}

//---------------------------------------------------------------------------------------------------
// AddDocument
//---------------------------------------------------------------------------------------------------
bool Library::AddDocument(Document& doc)
{
	return m_Documents.AddDocument(doc);
}

//---------------------------------------------------------------------------------------------------
// RemoveDocument
//---------------------------------------------------------------------------------------------------
Document* Library::AddDocument(const std::wstring& path, const std::wstring& keywords, bool runScanOnFolders)
{
	if (path.size() < 1)
		return NULL;

	Document* doc = NULL;

	if (PathIsURL(path.c_str()))
	{
		doc = new Document();

		doc->m_path = path;
		doc->m_pathLower = toLower(doc->m_path);
		doc->SetKeywords(keywords);

		doc->m_hash = hashString(path);

		doc->m_timestamp = L"";
	}
	else
	{
		WIN32_FILE_ATTRIBUTE_DATA fileInfo;

		if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fileInfo))
			return NULL;

		if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		{
			AddScanPath(path, runScanOnFolders);
			return NULL;
		}

		doc = new Document();

		doc->m_path = path;
		doc->m_pathLower = toLower(doc->m_path);
		doc->SetKeywords(keywords);

		doc->m_hash = fileInfo.nFileSizeLow;

		FILETIME fileTime = fileInfo.ftCreationTime;
	}

	Document* existingDoc = NULL;

	if (doc && !m_Documents.AddDocument(*doc, &existingDoc))
	{
		delete doc;
		doc = existingDoc;
	}

	return doc;
}

//---------------------------------------------------------------------------------------------------
// RemoveDocument
//---------------------------------------------------------------------------------------------------
void Library::RemoveDocument(const std::wstring& path)
{
	Document* ptr = m_Documents.RemoveDocument(path);

	if (ptr)
	{
		delete ptr;
	}
}

//---------------------------------------------------------------------------------------------------
// Clear
//---------------------------------------------------------------------------------------------------
void Documents::Clear()
{
	m_Documents.clear();
}

//---------------------------------------------------------------------------------------------------
// AddDocument
//---------------------------------------------------------------------------------------------------
bool Documents::AddDocument(Document& doc, Document** existingDoc)
{
	if (existingDoc)
	{
		(*existingDoc) = NULL;
	}

	for (DocumentList::iterator itr = m_Documents.begin(); itr != m_Documents.end(); ++itr)
	{
		if ((*itr)->m_hash == doc.m_hash || ((*itr)->m_pathLower.size() == doc.m_pathLower.size() && (*itr)->m_pathLower == doc.m_pathLower))
		{
			// Merge keywords
			std::set<std::wstring> keywords;

			std::vector<std::wstring> set1 = tokenSplit((*itr)->m_keywords);
			std::vector<std::wstring> set2 = tokenSplit(doc.m_keywords);
			std::vector<std::wstring> set3 = tokenSplit(doc.m_pathLower, L"\\/");

			keywords.insert(set1.begin(), set1.end());
			keywords.insert(set2.begin(), set2.end());
			keywords.insert(set3.begin(), set3.end());

			std::vector<std::wstring> setFinal(keywords.begin(), keywords.end());

			(*itr)->m_keywords = tokenMerge(setFinal);

			if (existingDoc)
			{
				(*existingDoc) = (*itr);
			}

			return false;
		}
	}

	m_Documents.push_back(&doc);

	return true;
}

//---------------------------------------------------------------------------------------------------
// RemoveDocument
//---------------------------------------------------------------------------------------------------
Document* Documents::RemoveDocument(const std::wstring& path)
{
	std::wstring pathLower = toLower(path);

	for (DocumentList::iterator itr = m_Documents.begin(); itr != m_Documents.end(); ++itr)
	{
		if ( (*itr)->m_pathLower == pathLower)
		{
			Document* ptr = (*itr);
			m_Documents.erase(itr);
			return ptr;
		}
	}

	return NULL;
}

//---------------------------------------------------------------------------------------------------
// Filter
//---------------------------------------------------------------------------------------------------
Documents Documents::Filter(const std::wstring& filterString) const
{
	if (filterString.empty())
		return m_Documents;

	std::vector<std::wstring> filterTokens1 = tokenSplit(toLower(filterString));
	std::set<std::wstring> filterTokens(filterTokens1.begin(), filterTokens1.end());

	Documents results;

	for (DocumentList::const_iterator itr = m_Documents.begin(); itr != m_Documents.end(); ++itr)
	{
		bool match = true;

		for (std::set<std::wstring>::iterator itr1 = filterTokens.begin(); itr1 != filterTokens.end(); ++itr1)
		{
			std::wstring subStr = (*itr1);

			if (	(*itr)->m_pathLower.find(subStr) == std::wstring::npos &&
					(*itr)->m_keywords.find(subStr) == std::wstring::npos)
			{
				match = false;
				break;
			}
		}

		if (match)
		{
			results.m_Documents.push_back(*itr);
		}
	}

	return results;
}

//---------------------------------------------------------------------------------------------------
// SetKeywords
//---------------------------------------------------------------------------------------------------
void Document::SetKeywords(const std::wstring& keywords)
{
	std::wstring keywordString = toLower(keywords);

	m_keywords.clear();
	m_authors.clear();
	m_company.clear();

	// Scan and extract author tags
	{
		std::wregex authorRegex(L"author\\s*:\\s*\\{(.*?)\\}");

		std::wstring keywordsWithoutAuthors;

		std::vector<std::wstring> authors;

		std::wsmatch results;
		while (std::regex_search(keywordString, results, authorRegex))
		{
 			authors.push_back(results[1].str());

			keywordsWithoutAuthors += results.prefix();
			keywordString = results.suffix();
		}

		keywordsWithoutAuthors += keywordString;

		keywordString = keywordsWithoutAuthors;

		std::sort(authors.begin(), authors.end());

		for (auto author: authors)
		{
			m_keywords += L"author:{" + author + L"}, ";
		}

		m_authors = tokenMerge(authors);
	}

	// Scan and extract company tags
	{
		std::wregex companyRegex(L"company\\s*:\\s*\\{(.*?)\\}");

		std::wstring keywordsWithoutCompanies;

		std::vector<std::wstring> companies;

		std::wsmatch results;
		while (std::regex_search(keywordString, results, companyRegex))
		{
 			companies.push_back(results[1].str());

			keywordsWithoutCompanies += results.prefix();
			keywordString = results.suffix();
		}

		keywordsWithoutCompanies += keywordString;

		keywordString = keywordsWithoutCompanies;

		std::sort(companies.begin(), companies.end());

		for (auto company: companies)
		{
			m_keywords += L"company:{" + company + L"}, ";
		}

		m_company = tokenMerge(companies);
	}

	// Split the rest into unique keywords
	{
		std::vector<std::wstring> v = tokenSplit(keywordString);

		std::set<std::wstring> s;
		s.insert(v.begin(), v.end());

		std::vector<std::wstring> unique(s.begin(), s.end());
		m_keywords += tokenMerge(unique);
	}
}
<commit_msg>hacky code to check if a path is a folder. the proper way is slowing down startup.<commit_after>#include "library.h"
#include <algorithm>
#include "dirent.h"
#include <tchar.h>
#include <sstream>
#include <iterator>
#include <set>
#include <string>
#include <cctype>
#include "Shlwapi.h"
#include <regex>

//---------------------------------------------------------------------------------------------------
// tokenSplit
//---------------------------------------------------------------------------------------------------
std::vector<std::wstring> tokenSplit(const std::wstring& str, const std::wstring& delim)
{
	std::vector<std::wstring> tokens;

	std::wstring::size_type p0 = str.find_first_not_of(delim, 0);
	std::wstring::size_type p1 = str.find_first_of(delim, p0);

	while (p0 != p1 && p0 != std::wstring::npos)
	{
		tokens.push_back(str.substr(p0, p1 - p0));

		p0 = str.find_first_not_of(delim, p1);
		p1 = str.find_first_of(delim, p0);
	}

	return tokens;
}

//---------------------------------------------------------------------------------------------------
// tokenMerge
//---------------------------------------------------------------------------------------------------
std::wstring tokenMerge(const std::vector<std::wstring>& tokens, const std::wstring& delim)
{
	std::wstring tokenString;

	for (std::vector<std::wstring>::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr)
	{
		if (itr == tokens.begin())
		{
			tokenString += *itr;
		}
		else
		{
			tokenString += delim + *itr;
		}
	}

	return tokenString;
}

//---------------------------------------------------------------------------------------------------
// toLower
//---------------------------------------------------------------------------------------------------
std::wstring toLower(const std::wstring& str)
{
	std::wstring temp = str;
	std::transform(str.begin(), str.end(), temp.begin(), ::tolower);
	//std::transform(str.begin(), str.end(), temp.begin(), [](unsigned char c) -> unsigned char { return std::toupper(c); });

	return temp;
}

//---------------------------------------------------------------------------------------------------
// hashString
// FNV www.isthe.com/chongo/tech/comp/fnv/
//---------------------------------------------------------------------------------------------------
unsigned int hashString(const std::wstring& str)
{
	static const unsigned int magic = 0x01000193;

	unsigned char *s = (unsigned char *)str.c_str();

	unsigned int hval = 0;

	unsigned int count = str.size() * sizeof(TCHAR);

	for (int i=0; i!=count; ++i)
	{
		hval *= magic;
		hval ^= *s++;
	}

	return hval;
}

//---------------------------------------------------------------------------------------------------
// hackyIsFolder
//---------------------------------------------------------------------------------------------------
bool hackyIsFolder(const std::wstring& path)
{
	int lastSlashPos = path.rfind('\\');
	int lastDotPos = path.rfind('.');

	if (lastSlashPos == path.length())
		return true;

	if (lastDotPos == -1)
		return true;

	if (lastDotPos > lastSlashPos)
		return false;

	return false;
}

//---------------------------------------------------------------------------------------------------
// Library
//---------------------------------------------------------------------------------------------------
Library::Library(const std::wstring& savePath)
{
	m_SavePath = savePath;
	m_SavePath += L"\\";
	m_SavePath += L"VGPLibrarian.cfg";
}

//---------------------------------------------------------------------------------------------------
// AddScanPath
//---------------------------------------------------------------------------------------------------
void Library::AddScanPath(const std::wstring& rootPath, bool runScan)
{
	std::wstring path = toLower(rootPath);

	if (path.rfind('\\') != path.length())
	{
		path += L"\\";
	}

	if (std::find(m_ScanPaths.begin(), m_ScanPaths.end(), path) == m_ScanPaths.end())
	{
		m_ScanPaths.push_back(path);
	}

	if (runScan)
	{
		ScanFolder(path);
	}
}

//---------------------------------------------------------------------------------------------------
// Scan
//---------------------------------------------------------------------------------------------------
void Library::Scan()
{
	for (PathArray::iterator itr = m_ScanPaths.begin(); itr != m_ScanPaths.end(); ++itr)
	{
		ScanFolder((*itr).c_str());
	}
}

//---------------------------------------------------------------------------------------------------
// Clear
//---------------------------------------------------------------------------------------------------
void Library::Clear()
{
	for (DocumentList::const_iterator itr = m_Documents.GetDocuments().begin(); itr != m_Documents.GetDocuments().end(); ++itr)
	{
		delete (*itr);
	}

	m_Documents.Clear();
}

//---------------------------------------------------------------------------------------------------
// ScanFolder
//---------------------------------------------------------------------------------------------------
void Library::ScanFolder(const std::wstring& path)
{
    _WDIR* dir = _wopendir(path.c_str());

	if (!dir)
		return;

	struct _wdirent* dirEntry;

	while ((dirEntry = _wreaddir(dir)) != NULL)
	{
		std::wstring fullPath = path + _T("\\") + dirEntry->d_name;

		switch (dirEntry->d_type)
		{
			case DT_REG:
			{
				AddDocument(fullPath, L"", false);
				break;
			}

			case DT_DIR:
				if (wcscmp(dirEntry->d_name, L".") != 0 && wcscmp(dirEntry->d_name, L"..") != 0)
				{
					ScanFolder(fullPath);
				}
			break;
		}
	}

	wclosedir(dir);
}

//---------------------------------------------------------------------------------------------------
// LoadMeta
//---------------------------------------------------------------------------------------------------
void Library::LoadMeta()
{
	FILE* hMetaFile;
	_wfopen_s(&hMetaFile, m_SavePath.c_str(), L"r,ccs=UTF-8");

	if (!hMetaFile)
		return;

	WCHAR buffer[1024];
	while (fgetws(buffer, 1024, hMetaFile))
	{
		std::wstring line = buffer;

		std::wstring::size_type quote1 = line.find(L"\"", 0);
		std::wstring::size_type quote2 = line.find(L"\"", quote1+1);
		std::wstring::size_type comma1 = line.find(L",", quote2 + 1);
		std::wstring::size_type newline = line.find(L"\n");

		std::wstring path = line.substr(quote1 + 1, quote2 - quote1 - 1);
		std::wstring keywords = line.substr(comma1 + 1, newline - comma1 - 1);

		AddDocument(path, keywords, false);
	}

	fclose(hMetaFile);
}

//---------------------------------------------------------------------------------------------------
// SaveMeta
//---------------------------------------------------------------------------------------------------
void Library::SaveMeta() const
{
	FILE* hFile;
	_wfopen_s(&hFile, m_SavePath.c_str(), L"w,ccs=UTF-8");

	if (!hFile)
		return;

	for (PathArray::const_iterator itr = m_ScanPaths.begin(); itr != m_ScanPaths.end(); ++itr)
	{
		fwprintf(hFile, L"\"%s\"\n", (*itr).c_str());
	}

	for (DocumentList::const_iterator itr = m_Documents.GetDocuments().begin(); itr != m_Documents.GetDocuments().end(); ++itr)
	{
		fwprintf(hFile, L"\"%s\",%s\n", (*itr)->m_path.c_str(), (*itr)->m_keywords.c_str());
	}

	fclose(hFile);
}

//---------------------------------------------------------------------------------------------------
// AddDocument
//---------------------------------------------------------------------------------------------------
bool Library::AddDocument(Document& doc)
{
	return m_Documents.AddDocument(doc);
}

//---------------------------------------------------------------------------------------------------
// RemoveDocument
//---------------------------------------------------------------------------------------------------
Document* Library::AddDocument(const std::wstring& path, const std::wstring& keywords, bool runScanOnFolders)
{
	if (path.size() < 1)
		return NULL;

	Document* doc = NULL;

	if (PathIsURL(path.c_str()))
	{
		doc = new Document();

		doc->m_path = path;
		doc->m_pathLower = toLower(doc->m_path);
		doc->SetKeywords(keywords);

		doc->m_hash = hashString(path);

		doc->m_timestamp = L"";
	}
	else
	{
		//WIN32_FILE_ATTRIBUTE_DATA fileInfo;

		//if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &fileInfo))
		//	return NULL;

		//if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
		if (hackyIsFolder(path))
		{
			AddScanPath(path, runScanOnFolders);
			return NULL;
		}

		doc = new Document();

		doc->m_path = path;
		doc->m_pathLower = toLower(doc->m_path);
		doc->SetKeywords(keywords);

		doc->m_hash = hashString(path);
	}

	Document* existingDoc = NULL;

	if (doc && !m_Documents.AddDocument(*doc, &existingDoc))
	{
		delete doc;
		doc = existingDoc;
	}

	return doc;
}

//---------------------------------------------------------------------------------------------------
// RemoveDocument
//---------------------------------------------------------------------------------------------------
void Library::RemoveDocument(const std::wstring& path)
{
	Document* ptr = m_Documents.RemoveDocument(path);

	if (ptr)
	{
		delete ptr;
	}
}

//---------------------------------------------------------------------------------------------------
// Clear
//---------------------------------------------------------------------------------------------------
void Documents::Clear()
{
	m_Documents.clear();
}

//---------------------------------------------------------------------------------------------------
// AddDocument
//---------------------------------------------------------------------------------------------------
bool Documents::AddDocument(Document& doc, Document** existingDoc)
{
	if (existingDoc)
	{
		(*existingDoc) = NULL;
	}

	for (DocumentList::iterator itr = m_Documents.begin(); itr != m_Documents.end(); ++itr)
	{
		if ((*itr)->m_hash == doc.m_hash || ((*itr)->m_pathLower.size() == doc.m_pathLower.size() && (*itr)->m_pathLower == doc.m_pathLower))
		{
			// Merge keywords
			std::set<std::wstring> keywords;

			std::vector<std::wstring> set1 = tokenSplit((*itr)->m_keywords);
			std::vector<std::wstring> set2 = tokenSplit(doc.m_keywords);
			std::vector<std::wstring> set3 = tokenSplit(doc.m_pathLower, L"\\/");

			keywords.insert(set1.begin(), set1.end());
			keywords.insert(set2.begin(), set2.end());
			keywords.insert(set3.begin(), set3.end());

			std::vector<std::wstring> setFinal(keywords.begin(), keywords.end());

			(*itr)->m_keywords = tokenMerge(setFinal);

			if (existingDoc)
			{
				(*existingDoc) = (*itr);
			}

			return false;
		}
	}

	m_Documents.push_back(&doc);

	return true;
}

//---------------------------------------------------------------------------------------------------
// RemoveDocument
//---------------------------------------------------------------------------------------------------
Document* Documents::RemoveDocument(const std::wstring& path)
{
	std::wstring pathLower = toLower(path);

	for (DocumentList::iterator itr = m_Documents.begin(); itr != m_Documents.end(); ++itr)
	{
		if ( (*itr)->m_pathLower == pathLower)
		{
			Document* ptr = (*itr);
			m_Documents.erase(itr);
			return ptr;
		}
	}

	return NULL;
}

//---------------------------------------------------------------------------------------------------
// Filter
//---------------------------------------------------------------------------------------------------
Documents Documents::Filter(const std::wstring& filterString) const
{
	if (filterString.empty())
		return m_Documents;

	std::vector<std::wstring> filterTokens1 = tokenSplit(toLower(filterString));
	std::set<std::wstring> filterTokens(filterTokens1.begin(), filterTokens1.end());

	Documents results;

	for (DocumentList::const_iterator itr = m_Documents.begin(); itr != m_Documents.end(); ++itr)
	{
		bool match = true;

		for (std::set<std::wstring>::iterator itr1 = filterTokens.begin(); itr1 != filterTokens.end(); ++itr1)
		{
			std::wstring subStr = (*itr1);

			if (	(*itr)->m_pathLower.find(subStr) == std::wstring::npos &&
					(*itr)->m_keywords.find(subStr) == std::wstring::npos)
			{
				match = false;
				break;
			}
		}

		if (match)
		{
			results.m_Documents.push_back(*itr);
		}
	}

	return results;
}

//---------------------------------------------------------------------------------------------------
// SetKeywords
//---------------------------------------------------------------------------------------------------
void Document::SetKeywords(const std::wstring& keywords)
{
	std::wstring keywordString = toLower(keywords);

	m_keywords.clear();
	m_authors.clear();
	m_company.clear();

	// Scan and extract author tags
	if (keywords.find(L"author") != -1)
	{
		std::wregex authorRegex(L"author\\s*:\\s*\\{(.*?)\\}");

		std::wstring keywordsWithoutAuthors;

		std::vector<std::wstring> authors;

		std::wsmatch results;
		while (std::regex_search(keywordString, results, authorRegex))
		{
 			authors.push_back(results[1].str());

			keywordsWithoutAuthors += results.prefix();
			keywordString = results.suffix();
		}

		keywordsWithoutAuthors += keywordString;

		keywordString = keywordsWithoutAuthors;

		std::sort(authors.begin(), authors.end());

		for (auto author: authors)
		{
			m_keywords += L"author:{" + author + L"}, ";
		}

		m_authors = tokenMerge(authors);
	}

	// Scan and extract company tags
	if (keywords.find(L"company") != -1)
	{
		std::wregex companyRegex(L"company\\s*:\\s*\\{(.*?)\\}");

		std::wstring keywordsWithoutCompanies;

		std::vector<std::wstring> companies;

		std::wsmatch results;
		while (std::regex_search(keywordString, results, companyRegex))
		{
 			companies.push_back(results[1].str());

			keywordsWithoutCompanies += results.prefix();
			keywordString = results.suffix();
		}

		keywordsWithoutCompanies += keywordString;

		keywordString = keywordsWithoutCompanies;

		std::sort(companies.begin(), companies.end());

		for (auto company: companies)
		{
			m_keywords += L"company:{" + company + L"}, ";
		}

		m_company = tokenMerge(companies);
	}

	// Split the rest into unique keywords
	{
		std::vector<std::wstring> v = tokenSplit(keywordString);

		std::set<std::wstring> s;
		s.insert(v.begin(), v.end());

		std::vector<std::wstring> unique(s.begin(), s.end());
		m_keywords += tokenMerge(unique);
	}
}
<|endoftext|>


<commit_before>#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>

#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/session.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_info.hpp>

namespace lt = libtorrent;
int main(int argc, char *argv[]) {
	if (argc != 2) {
		std::cerr << "usage: " << argv[0] << " <magnet-url>" <<
			std::endl;
		return 1;
	}

	lt::session ses;
	lt::add_torrent_params atp;
	atp.url = argv[1];
	atp.upload_mode = true;
	atp.auto_managed = false;
	atp.paused = false;
	atp.save_path = "."; // save in current dir
	/*
	// .flags duplicate .upload_mode/auto_managed/paused
	// functionality:
	atp.flags = lt::add_torrent_params::flag_update_subscribe
		| lt::add_torrent_params::flag_upload_mode
		| lt::add_torrent_params::flag_apply_ip_filter;
	*/
	lt::torrent_handle torh = ses.add_torrent(atp);
	std::cout << atp.url << ":";
	for (;;) {
		std::deque<lt::alert*> alerts;
		ses.pop_alerts(&alerts);
		for (lt::alert const* a : alerts) {
			std::cout << a->message() << std::endl;
			// quit on error/finish:
			if (lt::alert_cast<lt::torrent_finished_alert>(a)
			|| lt::alert_cast<lt::torrent_error_alert>(a)) {
				goto done1;
			};
		}
		if (torh.has_metadata()) {
			ses.pause();
			lt::torrent_info tinf =
				torh.get_torrent_info();
			std::cout << tinf.name();
			std::cout.flush();
			std::ofstream ofs(tinf.name() + ".torrent");
			std::ostream_iterator<char> ofsi(ofs);
			lt::bencode(ofsi, lt::create_torrent(tinf).generate());
			ofs.close();
			ses.remove_torrent(torh);
			goto done0;
		};
		std::cout << ".";
		std::cout.flush();
		std::this_thread::sleep_for(
			std::chrono::milliseconds(1000));
	}
done0:
	std::cout << std::endl;
	return 0;
done1:
	std::cout << std::endl;
	return 1;
}

// vi: set sw=8 ts=8 noet tw=77:
<commit_msg>Break long line.<commit_after>#include <iostream>
#include <fstream>
#include <thread>
#include <chrono>

#include <libtorrent/add_torrent_params.hpp>
#include <libtorrent/alert_types.hpp>
#include <libtorrent/bencode.hpp>
#include <libtorrent/create_torrent.hpp>
#include <libtorrent/session.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <libtorrent/torrent_info.hpp>

namespace lt = libtorrent;
int main(int argc, char *argv[]) {
	if (argc != 2) {
		std::cerr << "usage: " << argv[0] << " <magnet-url>" <<
			std::endl;
		return 1;
	}

	lt::session ses;
	lt::add_torrent_params atp;
	atp.url = argv[1];
	atp.upload_mode = true;
	atp.auto_managed = false;
	atp.paused = false;
	atp.save_path = "."; // save in current dir
	/*
	// .flags duplicate .upload_mode/auto_managed/paused
	// functionality:
	atp.flags = lt::add_torrent_params::flag_update_subscribe
		| lt::add_torrent_params::flag_upload_mode
		| lt::add_torrent_params::flag_apply_ip_filter;
	*/
	lt::torrent_handle torh = ses.add_torrent(atp);
	std::cout << atp.url << ":";
	for (;;) {
		std::deque<lt::alert*> alerts;
		ses.pop_alerts(&alerts);
		for (lt::alert const* a : alerts) {
			std::cout << a->message() << std::endl;
			// quit on error/finish:
			if (lt::alert_cast<lt::torrent_finished_alert>(a)
			|| lt::alert_cast<lt::torrent_error_alert>(a)) {
				goto done1;
			};
		}
		if (torh.has_metadata()) {
			ses.pause();
			lt::torrent_info tinf =
				torh.get_torrent_info();
			std::cout << tinf.name();
			std::cout.flush();
			std::ofstream ofs(tinf.name() + ".torrent");
			std::ostream_iterator<char> ofsi(ofs);
			lt::bencode(ofsi, lt::create_torrent(tinf)
					.generate());
			ofs.close();
			ses.remove_torrent(torh);
			goto done0;
		};
		std::cout << ".";
		std::cout.flush();
		std::this_thread::sleep_for(
			std::chrono::milliseconds(1000));
	}
done0:
	std::cout << std::endl;
	return 0;
done1:
	std::cout << std::endl;
	return 1;
}

// vi: set sw=8 ts=8 noet tw=77:
<|endoftext|>


<commit_before>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: %cling %s | FileCheck %s
#include <cmath>
#include <stdio.h>

struct S{int i;};
S s = {12 };

typedef struct {int i;} T;

struct U{void f() const {};};

struct V{V(): v(12) {}; int v; };

int i = 12;
float f = sin(12);
int j = i;

void decls() {
   int arg1 = 17, arg2 = 42, add = -1;
#ifdef __linux__ && (__x86_64 || __i686 || __i386)
   __asm__ ( "addl %%ebx, %%eax;" : "=a" (add) : "a" (arg1) , "b" (arg2) );
#else
   add = arg1 + arg2;
#endif
   printf("result=%d\n", add); // CHECK:result=59
   printf("j=%d\n",j); // CHECK:j=12
}
<commit_msg>Fix spelling of CPP check.<commit_after>//------------------------------------------------------------------------------
// CLING - the C++ LLVM-based InterpreterG :)
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------

// RUN: %cling %s | FileCheck %s
#include <cmath>
#include <stdio.h>

struct S{int i;};
S s = {12 };

typedef struct {int i;} T;

struct U{void f() const {};};

struct V{V(): v(12) {}; int v; };

int i = 12;
float f = sin(12);
int j = i;

void decls() {
   int arg1 = 17, arg2 = 42, add = -1;
#if defined(__linux__) && (__x86_64 || __i686 || __i386)
   __asm__ ( "addl %%ebx, %%eax;" : "=a" (add) : "a" (arg1) , "b" (arg2) );
#else
   add = arg1 + arg2;
#endif
   printf("result=%d\n", add); // CHECK:result=59
   printf("j=%d\n",j); // CHECK:j=12
}
<|endoftext|>


<commit_before>/*
    log4cxx is C++ Log manager like log4js
    (C) 2016 SiLeader.
*/

// Version 1.0.0-release
#pragma once

#include <string>
#include <fstream>
#include <cctype>
#include <chrono>
#include <unordered_map>
#include <sstream>

#include "picojson.hpp"

namespace log4cxx {
    namespace detail {
        enum class Level {
            Fatal=5, Error=4, Warn=3, Info=2, Debug=1, Trace=0, Off=6,
        };
        inline Level convert_string_to_level(const std::string& ls) {
            std::string lev;
            std::transform(ls.begin(), ls.end(), lev.begin(), ::tolower);
            if(lev=="off") {
                return Level::Off;
            }else if(lev=="trace") {
                return Level::Trace;
            }else if(lev=="debug") {
                return Level::Debug;
            }else if(lev=="info") {
                return Level::Info;
            }else if(lev=="warn") {
                return Level::Warn;
            }else if(lev=="error") {
                return Level::Error;
            }else if(lev=="fatal") {
                return Level::Fatal;
            }else{
                return Level::Trace;
            }
        }
        namespace time {
            struct Time {
                unsigned int year, month, day, hour, minute, second, nanosec;
            };
            inline std::string nanoduration_to_time(/*std::chrono::nanoseconds nsec*/std::chrono::high_resolution_clock::time_point& tp) {
                //Time tt;
                auto nsec=std::chrono::duration_cast<std::chrono::nanoseconds>(tp.time_since_epoch());

                auto sec=std::chrono::duration_cast<std::chrono::seconds>(nsec);
                auto ns=std::chrono::duration_cast<std::chrono::nanoseconds>(nsec-sec).count();

                auto tim=std::chrono::system_clock::to_time_t(tp);
                struct tm ttm, *ptm;
                ptm=gmtime_r(&tim, &ttm);

                char buf[256]={0};
                strftime(buf, 256, "%Y:%m:%d %H:%M:%S.", ptm);
                std::string str=buf;
                str+="UTC ";
                str+=std::to_string(ns);
                //return tt;
                return str;
            }
        } /* time */
    } /* detail */

    class logger {
    private:
        std::string m_path, m_category;
        bool m_syslog, m_console;
        detail::Level m_level;

        std::ofstream m_fout;

        void m_write(const std::string& msg) {
            auto now=std::chrono::high_resolution_clock::now();
            auto ttstr=detail::time::nanoduration_to_time(now);

            std::stringstream ss;
            ss<<"["<<ttstr<<"] "<<msg<<std::flush;

            if(m_console) {
                std::cout<<ss.str()<<std::endl;
            }
            m_fout<<"["<<ttstr<<"] "<<msg<<std::endl;
        }
    public:
        explicit logger()=default;
        explicit logger(const logger&)=default;
        explicit logger(logger&)=default;
        logger(const std::string& path, const std::string& category, bool syslog, bool console, detail::Level level) noexcept
            : m_path(path), m_category(category), m_syslog(syslog), m_console(console), m_level(level), m_fout(m_path, std::ios::app) {}

        logger& operator=(const logger&)=default;
        logger& operator=(logger&&)=default;

        void trace(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Trace) {
                m_write(std::string("[TRACE] ")+m_category+" - "+msg);
            }
        }

        void debug(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Debug) {
                m_write(std::string("[DEBUG] ")+m_category+" - "+msg);
            }
        }

        void info(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Info) {
                m_write(std::string("[INFO] ")+m_category+" - "+msg);
            }
        }

        void warn(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Warn) {
                m_write(std::string("[WARN] ")+m_category+" - "+msg);
            }
        }

        void error(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Error) {
                m_write(std::string("[ERROR] ")+m_category+" - "+msg);
            }
        }

        void fatal(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Fatal) {
                m_write(std::string("[FATAL] ")+m_category+" - "+msg);
            }
        }
    };

    class log4cxx {
    private:
        struct Setting {
            std::string path, category;
            bool write_to_syslog, write_to_console;
            detail::Level level;
            logger log;
        };
        std::string m_setting_file;
        std::unordered_map<std::string, Setting> m_setting;

        void m_load()noexcept {
            std::ifstream fin(m_setting_file);
            picojson::value val;
            fin>>val;
            fin.close();
            {
                auto&& appenders=val.get<picojson::object>()["appenders"].get<picojson::array>();
                Setting set;
                for(auto&& a : appenders) {
                    set.category=a.get<picojson::object>()["category"].get<std::string>();
                    set.path=a.get<picojson::object>()["filename"].get<std::string>();

                    if(a.contains("syslog")) {
                        set.write_to_syslog=a.get<picojson::object>()["syslog"].get<bool>();
                    }else{
                        set.write_to_syslog=false;
                    }

                    if(a.contains("console")) {
                        set.write_to_console=a.get<picojson::object>()["console"].get<bool>();
                    }else{
                        set.write_to_console=true;
                    }

                    if(a.contains("level")) {
                        set.level=detail::convert_string_to_level(a.get<picojson::object>()["level"].get<std::string>());
                    }else{
                        set.level=detail::Level::Trace;
                    }
                    set.log=logger(set.path, set.category, set.write_to_syslog, set.write_to_console, set.level);
                    m_setting[set.category]=std::move(set);
                }
            }
        }
    public:
        log4cxx(const std::string& setting_file="log_config.json")noexcept
            : m_setting_file(setting_file)
        {
            m_load();
        }

        void reload()noexcept {
            m_load();
        }

        logger& get_logger(const std::string& category)noexcept {
            return m_setting[category].log;
        }
    };
} /* log4cxx */
<commit_msg>bug fix<commit_after>/*
    log4cxx is C++ Log manager like log4js
    (C) 2016 SiLeader.
*/

// Version 1.0.0-release
#pragma once

#include <string>
#include <fstream>
#include <cctype>
#include <chrono>
#include <unordered_map>
#include <sstream>

#include "picojson.hpp"

namespace log4cxx {
    namespace detail {
        enum class Level {
            Fatal=5, Error=4, Warn=3, Info=2, Debug=1, Trace=0, Off=6,
        };
        inline Level convert_string_to_level(const std::string& ls) {
            std::string lev;
            std::transform(ls.begin(), ls.end(), lev.begin(), ::tolower);
            if(lev=="off") {
                return Level::Off;
            }else if(lev=="trace") {
                return Level::Trace;
            }else if(lev=="debug") {
                return Level::Debug;
            }else if(lev=="info") {
                return Level::Info;
            }else if(lev=="warn") {
                return Level::Warn;
            }else if(lev=="error") {
                return Level::Error;
            }else if(lev=="fatal") {
                return Level::Fatal;
            }else{
                return Level::Trace;
            }
        }
        namespace time {
            struct Time {
                unsigned int year, month, day, hour, minute, second, nanosec;
            };
            inline std::string nanoduration_to_time(/*std::chrono::nanoseconds nsec*/std::chrono::high_resolution_clock::time_point& tp) {
                //Time tt;
                auto nsec=std::chrono::duration_cast<std::chrono::nanoseconds>(tp.time_since_epoch());

                auto sec=std::chrono::duration_cast<std::chrono::seconds>(nsec);
                auto ns=std::chrono::duration_cast<std::chrono::nanoseconds>(nsec-sec).count();

                auto tim=std::chrono::system_clock::to_time_t(tp);
                struct tm ttm, *ptm;
                ptm=gmtime_r(&tim, &ttm);

                char buf[256]={0};
                strftime(buf, 256, "%Y:%m:%d %H:%M:%S.", ptm);
                std::string str="UTC ";
                str+=buf;
                str+=std::to_string(ns);
                //return tt;
                return str;
            }
        } /* time */
    } /* detail */

    class logger {
    private:
        std::string m_path, m_category;
        bool m_syslog, m_console;
        detail::Level m_level;

        std::ofstream m_fout;

        void m_write(const std::string& msg) {
            auto now=std::chrono::high_resolution_clock::now();
            auto ttstr=detail::time::nanoduration_to_time(now);

            std::stringstream ss;
            ss<<"["<<ttstr<<"] "<<msg<<std::flush;

            if(m_console) {
                std::cout<<ss.str()<<std::endl;
            }
            m_fout<<"["<<ttstr<<"] "<<msg<<std::endl;
        }
    public:
        explicit logger()=default;
        explicit logger(const logger&)=default;
        explicit logger(logger&)=default;
        logger(const std::string& path, const std::string& category, bool syslog, bool console, detail::Level level) noexcept
            : m_path(path), m_category(category), m_syslog(syslog), m_console(console), m_level(level), m_fout(m_path, std::ios::app) {}

        logger& operator=(const logger&)=default;
        logger& operator=(logger&&)=default;

        void trace(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Trace) {
                m_write(std::string("[TRACE] ")+m_category+" - "+msg);
            }
        }

        void debug(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Debug) {
                m_write(std::string("[DEBUG] ")+m_category+" - "+msg);
            }
        }

        void info(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Info) {
                m_write(std::string("[INFO] ")+m_category+" - "+msg);
            }
        }

        void warn(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Warn) {
                m_write(std::string("[WARN] ")+m_category+" - "+msg);
            }
        }

        void error(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Error) {
                m_write(std::string("[ERROR] ")+m_category+" - "+msg);
            }
        }

        void fatal(const std::string& msg)noexcept {
            if(m_level<=detail::Level::Fatal) {
                m_write(std::string("[FATAL] ")+m_category+" - "+msg);
            }
        }
    };

    class log4cxx {
    private:
        struct Setting {
            std::string path, category;
            bool write_to_syslog, write_to_console;
            detail::Level level;
            logger log;
        };
        std::string m_setting_file;
        std::unordered_map<std::string, Setting> m_setting;

        void m_load()noexcept {
            std::ifstream fin(m_setting_file);
            picojson::value val;
            fin>>val;
            fin.close();
            {
                auto&& appenders=val.get<picojson::object>()["appenders"].get<picojson::array>();
                Setting set;
                for(auto&& a : appenders) {
                    set.category=a.get<picojson::object>()["category"].get<std::string>();
                    set.path=a.get<picojson::object>()["filename"].get<std::string>();

                    if(a.contains("syslog")) {
                        set.write_to_syslog=a.get<picojson::object>()["syslog"].get<bool>();
                    }else{
                        set.write_to_syslog=false;
                    }

                    if(a.contains("console")) {
                        set.write_to_console=a.get<picojson::object>()["console"].get<bool>();
                    }else{
                        set.write_to_console=true;
                    }

                    if(a.contains("level")) {
                        set.level=detail::convert_string_to_level(a.get<picojson::object>()["level"].get<std::string>());
                    }else{
                        set.level=detail::Level::Trace;
                    }
                    set.log=logger(set.path, set.category, set.write_to_syslog, set.write_to_console, set.level);
                    m_setting[set.category]=std::move(set);
                }
            }
        }
    public:
        log4cxx(const std::string& setting_file="log_config.json")noexcept
            : m_setting_file(setting_file)
        {
            m_load();
        }

        void reload()noexcept {
            m_load();
        }

        logger& get_logger(const std::string& category)noexcept {
            return m_setting[category].log;
        }
    };
} /* log4cxx */
<|endoftext|>


<commit_before>#include "tz/core/time.hpp"
#include "tz/core/assert.hpp"
#include <thread>

void test_waits()
{
	using namespace tz::literals;
	using namespace std::chrono_literals;
	tz::Delay d{10_ms};
	tz_assert(!d.done(), "Delay of 10 millis was apparantly done instantly.");
	std::this_thread::sleep_for(11ms);
	tz_assert(d.done(), "Delay of 10 millis was not done after a this_thread_sleep_for 11ms");

	d.reset();
	tz_assert(!d.done(), "Delay of 10 millis was apparantly done instantly.");
	tz_assert(!d, "Delay of 10 millis was apparantly done instantly.");
	std::this_thread::sleep_for(11ms);
	tz_assert(d.done(), "Delay of 10 millis was not done after a this_thread_sleep_for 11ms");
	tz_assert(d, "Delay of 10 millis was not done after a this_thread_sleep_for 10ms");
}

int main()
{
	using namespace tz::literals;
	constexpr tz::Duration s = 1_s;
	static_assert(s == 1000_ms);
	static_assert(s == 1000000_us);
	static_assert(s == 1000000000_ns);
	static_assert(s.seconds<int>() == 1);
	static_assert(s.seconds<float>() == 1.0f);
	static_assert(s.seconds<unsigned int>() == (1000_ms).seconds<float>());
	static_assert(s > 500_ms && s < 2_s);
	test_waits();
}
<commit_msg>- Remove the ridiculous `wait test` in tz_time_test<commit_after>#include "tz/core/time.hpp"
#include "tz/core/assert.hpp"
#include <thread>

int main()
{
	using namespace tz::literals;
	constexpr tz::Duration s = 1_s;
	static_assert(s == 1000_ms);
	static_assert(s == 1000000_us);
	static_assert(s == 1000000000_ns);
	static_assert(s.seconds<int>() == 1);
	static_assert(s.seconds<float>() == 1.0f);
	static_assert(s.seconds<unsigned int>() == (1000_ms).seconds<float>());
	static_assert(s > 500_ms && s < 2_s);
}
<|endoftext|>


<commit_before>//
// Copyright RIME Developers
// Distributed under the BSD License
//
// 2011-07-05 GONG Chen <chen.sst@gmail.com>
//
#include <gtest/gtest.h>
#include <rime/common.h>
#include <rime/algo/encoder.h>
#include <rime/algo/syllabifier.h>
#include <rime/dict/dictionary.h>
#include <rime/dict/dict_compiler.h>

class RimeDictionaryTest : public ::testing::Test {
 public:
  virtual void SetUp() {
    if (!dict_) {
      dict_.reset(new rime::Dictionary(
          "dictionary_test",
          {},
          {rime::New<rime::Table>("dictionary_test.table.bin")},
          rime::New<rime::Prism>("dictionary_test.prism.bin")));
    }
    if (!rebuilt_) {
      dict_->Remove();
      rime::DictCompiler dict_compiler(dict_.get());
      dict_compiler.Compile("");  // no schema file
      rebuilt_ = true;
    }
    dict_->Load();
  }
  virtual void TearDown() {
    dict_.reset();
  }
 protected:
  static rime::the<rime::Dictionary> dict_;
  static bool rebuilt_;
};

rime::the<rime::Dictionary> RimeDictionaryTest::dict_;
bool RimeDictionaryTest::rebuilt_ = false;

TEST_F(RimeDictionaryTest, Ready) {
  EXPECT_TRUE(dict_->loaded());
}

TEST_F(RimeDictionaryTest, SimpleLookup) {
  ASSERT_TRUE(dict_->loaded());
  rime::DictEntryIterator it;
  dict_->LookupWords(&it, "zhong", false);
  ASSERT_FALSE(it.exhausted());
  EXPECT_EQ("\xe4\xb8\xad", it.Peek()->text);  // 中
  ASSERT_EQ(1, it.Peek()->code.size());
  rime::RawCode raw_code;
  ASSERT_TRUE(dict_->Decode(it.Peek()->code, &raw_code));
  EXPECT_EQ("zhong", raw_code.ToString());
}

TEST_F(RimeDictionaryTest, PredictiveLookup) {
  ASSERT_TRUE(dict_->loaded());
  rime::DictEntryIterator it;
  dict_->LookupWords(&it, "z", true);
  ASSERT_FALSE(it.exhausted());
  EXPECT_EQ("\xe5\x92\x8b", it.Peek()->text);  // 咋
  ASSERT_EQ(1, it.Peek()->code.size());
  rime::RawCode raw_code;
  ASSERT_TRUE(dict_->Decode(it.Peek()->code, &raw_code));
  EXPECT_EQ("za", raw_code.ToString());
}

TEST_F(RimeDictionaryTest, ScriptLookup) {
  ASSERT_TRUE(dict_->loaded());
  rime::SyllableGraph g;
  rime::Syllabifier s;
  rime::string input("shurufa");
  ASSERT_TRUE(s.BuildSyllableGraph(input, *dict_->prism(), &g) > 0);
  EXPECT_EQ(g.interpreted_length, g.input_length);
  auto c = dict_->Lookup(g, 0);
  ASSERT_TRUE(bool(c));

  ASSERT_TRUE(c->find(3) != c->end());
  rime::DictEntryIterator d3((*c)[3]);
  EXPECT_FALSE(d3.exhausted());
  auto e1 = d3.Peek();
  ASSERT_TRUE(bool(e1));
  EXPECT_EQ(1, e1->code.size());
  EXPECT_EQ(3, e1->text.length());
  EXPECT_TRUE(d3.Next());

  ASSERT_TRUE(c->find(5) != c->end());
  rime::DictEntryIterator d5((*c)[5]);
  EXPECT_FALSE(d5.exhausted());
  auto e2 = d5.Peek();
  ASSERT_TRUE(bool(e2));
  EXPECT_EQ(2, e2->code.size());

  ASSERT_TRUE(c->find(7) != c->end());
  rime::DictEntryIterator d7((*c)[7]);
  EXPECT_FALSE(d7.exhausted());
  auto e3 = d7.Peek();
  ASSERT_TRUE(bool(e3));
  EXPECT_EQ(3, e3->code.size());
  EXPECT_EQ(9, e3->text.length());
  EXPECT_FALSE(d7.Next());
}
<commit_msg>Revert "chore(dictionary_test): fix Windows linking error in test code"<commit_after>//
// Copyright RIME Developers
// Distributed under the BSD License
//
// 2011-07-05 GONG Chen <chen.sst@gmail.com>
//
#include <gtest/gtest.h>
#include <rime/common.h>
#include <rime/algo/encoder.h>
#include <rime/algo/syllabifier.h>
#include <rime/dict/dictionary.h>
#include <rime/dict/dict_compiler.h>

class RimeDictionaryTest : public ::testing::Test {
 public:
  virtual void SetUp() {
    if (!dict_) {
      dict_.reset(new rime::Dictionary(
          "dictionary_test",
          {},
          {rime::New<rime::Table>("dictionary_test.table.bin")},
          rime::New<rime::Prism>("dictionary_test.prism.bin")));
    }
    if (!rebuilt_) {
      dict_->Remove();
      rime::DictCompiler dict_compiler(dict_.get());
      dict_compiler.Compile("");  // no schema file
      rebuilt_ = true;
    }
    dict_->Load();
  }
  virtual void TearDown() {
    dict_.reset();
  }
 protected:
  static rime::the<rime::Dictionary> dict_;
  static bool rebuilt_;
};

rime::the<rime::Dictionary> RimeDictionaryTest::dict_;
bool RimeDictionaryTest::rebuilt_ = false;

TEST_F(RimeDictionaryTest, Ready) {
  EXPECT_TRUE(dict_->loaded());
}

TEST_F(RimeDictionaryTest, SimpleLookup) {
  ASSERT_TRUE(dict_->loaded());
  rime::DictEntryIterator it;
  dict_->LookupWords(&it, "zhong", false);
  ASSERT_FALSE(it.exhausted());
  EXPECT_EQ("\xe4\xb8\xad", it.Peek()->text);  // 中
  ASSERT_EQ(1, it.Peek()->code.size());
  rime::RawCode raw_code;
  ASSERT_TRUE(dict_->Decode(it.Peek()->code, &raw_code));
  EXPECT_EQ("zhong", raw_code.ToString());
}

TEST_F(RimeDictionaryTest, PredictiveLookup) {
  ASSERT_TRUE(dict_->loaded());
  rime::DictEntryIterator it;
  dict_->LookupWords(&it, "z", true);
  ASSERT_FALSE(it.exhausted());
  EXPECT_EQ("\xe5\x92\x8b", it.Peek()->text);  // 咋
  ASSERT_EQ(1, it.Peek()->code.size());
  rime::RawCode raw_code;
  ASSERT_TRUE(dict_->Decode(it.Peek()->code, &raw_code));
  EXPECT_EQ("za", raw_code.ToString());
}

TEST_F(RimeDictionaryTest, ScriptLookup) {
  ASSERT_TRUE(dict_->loaded());
  rime::SyllableGraph g;
  rime::Syllabifier s;
  rime::string input("shurufa");
  ASSERT_TRUE(s.BuildSyllableGraph(input, *dict_->prism(), &g) > 0);
  EXPECT_EQ(g.interpreted_length, g.input_length);
  auto c = dict_->Lookup(g, 0);
  ASSERT_TRUE(bool(c));

  ASSERT_TRUE(c->find(3) != c->end());
  rime::DictEntryIterator& d3((*c)[3]);
  EXPECT_FALSE(d3.exhausted());
  auto e1 = d3.Peek();
  ASSERT_TRUE(bool(e1));
  EXPECT_EQ(1, e1->code.size());
  EXPECT_EQ(3, e1->text.length());
  EXPECT_TRUE(d3.Next());

  ASSERT_TRUE(c->find(5) != c->end());
  rime::DictEntryIterator& d5((*c)[5]);
  EXPECT_FALSE(d5.exhausted());
  auto e2 = d5.Peek();
  ASSERT_TRUE(bool(e2));
  EXPECT_EQ(2, e2->code.size());

  ASSERT_TRUE(c->find(7) != c->end());
  rime::DictEntryIterator& d7((*c)[7]);
  EXPECT_FALSE(d7.exhausted());
  auto e3 = d7.Peek();
  ASSERT_TRUE(bool(e3));
  EXPECT_EQ(3, e3->code.size());
  EXPECT_EQ(9, e3->text.length());
  EXPECT_FALSE(d7.Next());
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#pragma once

#include <string>
#include <cstddef>
#include <cinttypes>
#include <algorithm>
#include <stdexcept>
#include <type_traits>

#include "caf/detail/comparable.hpp"

namespace caf {

/// An intrusive, reference counting smart pointer implementation.
/// @relates ref_counted
template <class T>
class intrusive_ptr {
public:
  using pointer = T*;
  using const_pointer = const T*;
  using element_type = T;
  using reference = T&;
  using const_reference = const T&;

  // tell actor_cast which semantic this type uses
  static constexpr bool has_weak_ptr_semantics = false;

  constexpr intrusive_ptr() noexcept : ptr_(nullptr) {
    // nop
  }

  intrusive_ptr(pointer raw_ptr, bool add_ref = true) noexcept {
    set_ptr(raw_ptr, add_ref);
  }

  intrusive_ptr(intrusive_ptr&& other) noexcept : ptr_(other.detach()) {
    // nop
  }

  intrusive_ptr(const intrusive_ptr& other) noexcept {
    set_ptr(other.get(), true);
  }

  template <class Y>
  intrusive_ptr(intrusive_ptr<Y> other) noexcept : ptr_(other.detach()) {
    static_assert(std::is_convertible<Y*, T*>::value,
                  "Y* is not assignable to T*");
  }

  ~intrusive_ptr() {
    if (ptr_)
      intrusive_ptr_release(ptr_);
  }

  void swap(intrusive_ptr& other) noexcept {
    std::swap(ptr_, other.ptr_);
  }

  /// Returns the raw pointer without modifying reference
  /// count and sets this to `nullptr`.
  pointer detach() noexcept {
    auto result = ptr_;
    if (result)
      ptr_ = nullptr;
    return result;
  }

  /// Returns the raw pointer without modifying reference
  /// count and sets this to `nullptr`.
  pointer release() noexcept {
    return detach();
  }

  void reset(pointer new_value = nullptr, bool add_ref = true) noexcept {
    auto old = ptr_;
    set_ptr(new_value, add_ref);
    if (old)
      intrusive_ptr_release(old);
  }

  intrusive_ptr& operator=(pointer ptr) noexcept {
    reset(ptr);
    return *this;
  }

  intrusive_ptr& operator=(intrusive_ptr other) noexcept {
    swap(other);
    return *this;
  }

  pointer get() const noexcept {
    return ptr_;
  }

  pointer operator->() const noexcept {
    return ptr_;
  }

  reference operator*() const noexcept {
    return *ptr_;
  }

  bool operator!() const noexcept {
    return !ptr_;
  }

  explicit operator bool() const noexcept {
    return ptr_ != nullptr;
  }

  ptrdiff_t compare(const_pointer ptr) const noexcept {
    return static_cast<ptrdiff_t>(get() - ptr);
  }

  ptrdiff_t compare(const intrusive_ptr& other) const noexcept {
    return compare(other.get());
  }

  ptrdiff_t compare(std::nullptr_t) const noexcept {
    return reinterpret_cast<ptrdiff_t>(get());
  }

  template <class C>
  intrusive_ptr<C> downcast() const noexcept {
    return (ptr_) ? dynamic_cast<C*>(get()) : nullptr;
  }

  template <class C>
  intrusive_ptr<C> upcast() const noexcept {
    return (ptr_) ? static_cast<C*>(get()) : nullptr;
  }

private:
  void set_ptr(pointer raw_ptr, bool add_ref) noexcept {
    ptr_ = raw_ptr;
    if (raw_ptr && add_ref)
      intrusive_ptr_add_ref(raw_ptr);
  }

  pointer ptr_;
};

// -- comparison to nullptr ----------------------------------------------------

/// @relates intrusive_ptr
template <class T>
bool operator==(const intrusive_ptr<T>& x, std::nullptr_t) {
  return !x;
}

/// @relates intrusive_ptr
template <class T>
bool operator==(std::nullptr_t, const intrusive_ptr<T>& x) {
  return !x;
}

/// @relates intrusive_ptr
template <class T>
bool operator!=(const intrusive_ptr<T>& x, std::nullptr_t) {
  return static_cast<bool>(x);
}

/// @relates intrusive_ptr
template <class T>
bool operator!=(std::nullptr_t, const intrusive_ptr<T>& x) {
  return static_cast<bool>(x);
}

// -- comparison to raw pointer ------------------------------------------------

/// @relates intrusive_ptr
template <class T>
bool operator==(const intrusive_ptr<T>& x, const T* y) {
  return x.get() == y;
}

/// @relates intrusive_ptr
template <class T>
bool operator==(const T* x, const intrusive_ptr<T>& y) {
  return x == y.get();
}

/// @relates intrusive_ptr
template <class T>
bool operator!=(const intrusive_ptr<T>& x, const T* y) {
  return x.get() != y;
}

/// @relates intrusive_ptr
template <class T>
bool operator!=(const T* x, const intrusive_ptr<T>& y) {
  return x != y.get();
}

// -- comparison to intrusive_pointer ------------------------------------------

/// @relates intrusive_ptr
template <class T, class U>
bool operator==(const intrusive_ptr<T>& x, const intrusive_ptr<U>& y) {
  return x.get() == y.get();
}

/// @relates intrusive_ptr
template <class T, class U>
bool operator!=(const intrusive_ptr<T>& x, const intrusive_ptr<U>& y) {
  return x.get() != y.get();
}

/// @relates intrusive_ptr
template <class T>
bool operator<(const intrusive_ptr<T>& x, const intrusive_ptr<T>& y) {
  return x.get() < y.get();
}

/// @relates intrusive_ptr
template <class T>
bool operator<(const intrusive_ptr<T>& x, const T* y) {
  return x.get() < y;
}
/// @relates intrusive_ptr
template <class T>
bool operator<(const T* x, const intrusive_ptr<T>& y) {
  return x < y.get();
}

template <class T>
std::string to_string(const intrusive_ptr<T>& x) {
  auto v = reinterpret_cast<uintptr_t>(x.get());
  // we convert to hex representation, i.e.,
  // one byte takes two characters + null terminator + "0x" prefix
  char buf[sizeof(v) * 2 + 3];
  sprintf(buf, "%" PRIxPTR, v);
  return buf;
}

} // namespace caf

<commit_msg>Disable operator== for unrelated intrusive_ptr<commit_after>/******************************************************************************
 *                       ____    _    _____                                   *
 *                      / ___|  / \  |  ___|    C++                           *
 *                     | |     / _ \ | |_       Actor                         *
 *                     | |___ / ___ \|  _|      Framework                     *
 *                      \____/_/   \_|_|                                      *
 *                                                                            *
 * Copyright 2011-2018 Dominik Charousset                                     *
 *                                                                            *
 * Distributed under the terms and conditions of the BSD 3-Clause License or  *
 * (at your option) under the terms and conditions of the Boost Software      *
 * License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE.       *
 *                                                                            *
 * If you did not receive a copy of the license files, see                    *
 * http://opensource.org/licenses/BSD-3-Clause and                            *
 * http://www.boost.org/LICENSE_1_0.txt.                                      *
 ******************************************************************************/

#pragma once

#include <string>
#include <cstddef>
#include <cinttypes>
#include <algorithm>
#include <stdexcept>
#include <type_traits>

#include "caf/detail/comparable.hpp"
#include "caf/detail/type_traits.hpp"

namespace caf {

/// An intrusive, reference counting smart pointer implementation.
/// @relates ref_counted
template <class T>
class intrusive_ptr {
public:
  using pointer = T*;
  using const_pointer = const T*;
  using element_type = T;
  using reference = T&;
  using const_reference = const T&;

  // tell actor_cast which semantic this type uses
  static constexpr bool has_weak_ptr_semantics = false;

  constexpr intrusive_ptr() noexcept : ptr_(nullptr) {
    // nop
  }

  intrusive_ptr(pointer raw_ptr, bool add_ref = true) noexcept {
    set_ptr(raw_ptr, add_ref);
  }

  intrusive_ptr(intrusive_ptr&& other) noexcept : ptr_(other.detach()) {
    // nop
  }

  intrusive_ptr(const intrusive_ptr& other) noexcept {
    set_ptr(other.get(), true);
  }

  template <class Y>
  intrusive_ptr(intrusive_ptr<Y> other) noexcept : ptr_(other.detach()) {
    static_assert(std::is_convertible<Y*, T*>::value,
                  "Y* is not assignable to T*");
  }

  ~intrusive_ptr() {
    if (ptr_)
      intrusive_ptr_release(ptr_);
  }

  void swap(intrusive_ptr& other) noexcept {
    std::swap(ptr_, other.ptr_);
  }

  /// Returns the raw pointer without modifying reference
  /// count and sets this to `nullptr`.
  pointer detach() noexcept {
    auto result = ptr_;
    if (result)
      ptr_ = nullptr;
    return result;
  }

  /// Returns the raw pointer without modifying reference
  /// count and sets this to `nullptr`.
  pointer release() noexcept {
    return detach();
  }

  void reset(pointer new_value = nullptr, bool add_ref = true) noexcept {
    auto old = ptr_;
    set_ptr(new_value, add_ref);
    if (old)
      intrusive_ptr_release(old);
  }

  intrusive_ptr& operator=(pointer ptr) noexcept {
    reset(ptr);
    return *this;
  }

  intrusive_ptr& operator=(intrusive_ptr other) noexcept {
    swap(other);
    return *this;
  }

  pointer get() const noexcept {
    return ptr_;
  }

  pointer operator->() const noexcept {
    return ptr_;
  }

  reference operator*() const noexcept {
    return *ptr_;
  }

  bool operator!() const noexcept {
    return !ptr_;
  }

  explicit operator bool() const noexcept {
    return ptr_ != nullptr;
  }

  ptrdiff_t compare(const_pointer ptr) const noexcept {
    return static_cast<ptrdiff_t>(get() - ptr);
  }

  ptrdiff_t compare(const intrusive_ptr& other) const noexcept {
    return compare(other.get());
  }

  ptrdiff_t compare(std::nullptr_t) const noexcept {
    return reinterpret_cast<ptrdiff_t>(get());
  }

  template <class C>
  intrusive_ptr<C> downcast() const noexcept {
    return (ptr_) ? dynamic_cast<C*>(get()) : nullptr;
  }

  template <class C>
  intrusive_ptr<C> upcast() const noexcept {
    return (ptr_) ? static_cast<C*>(get()) : nullptr;
  }

private:
  void set_ptr(pointer raw_ptr, bool add_ref) noexcept {
    ptr_ = raw_ptr;
    if (raw_ptr && add_ref)
      intrusive_ptr_add_ref(raw_ptr);
  }

  pointer ptr_;
};

// -- comparison to nullptr ----------------------------------------------------

/// @relates intrusive_ptr
template <class T>
bool operator==(const intrusive_ptr<T>& x, std::nullptr_t) {
  return !x;
}

/// @relates intrusive_ptr
template <class T>
bool operator==(std::nullptr_t, const intrusive_ptr<T>& x) {
  return !x;
}

/// @relates intrusive_ptr
template <class T>
bool operator!=(const intrusive_ptr<T>& x, std::nullptr_t) {
  return static_cast<bool>(x);
}

/// @relates intrusive_ptr
template <class T>
bool operator!=(std::nullptr_t, const intrusive_ptr<T>& x) {
  return static_cast<bool>(x);
}

// -- comparison to raw pointer ------------------------------------------------

/// @relates intrusive_ptr
template <class T>
bool operator==(const intrusive_ptr<T>& x, const T* y) {
  return x.get() == y;
}

/// @relates intrusive_ptr
template <class T>
bool operator==(const T* x, const intrusive_ptr<T>& y) {
  return x == y.get();
}

/// @relates intrusive_ptr
template <class T>
bool operator!=(const intrusive_ptr<T>& x, const T* y) {
  return x.get() != y;
}

/// @relates intrusive_ptr
template <class T>
bool operator!=(const T* x, const intrusive_ptr<T>& y) {
  return x != y.get();
}

// -- comparison to intrusive_pointer ------------------------------------------

/// @relates intrusive_ptr
template <class T, class U>
detail::enable_if_t<detail::is_comparable<T*, U*>::value, bool>
operator==(const intrusive_ptr<T>& x, const intrusive_ptr<U>& y) {
  return x.get() == y.get();
}

/// @relates intrusive_ptr
template <class T, class U>
detail::enable_if_t<detail::is_comparable<T*, U*>::value, bool>
operator!=(const intrusive_ptr<T>& x, const intrusive_ptr<U>& y) {
  return x.get() != y.get();
}

/// @relates intrusive_ptr
template <class T>
bool operator<(const intrusive_ptr<T>& x, const intrusive_ptr<T>& y) {
  return x.get() < y.get();
}

/// @relates intrusive_ptr
template <class T>
bool operator<(const intrusive_ptr<T>& x, const T* y) {
  return x.get() < y;
}
/// @relates intrusive_ptr
template <class T>
bool operator<(const T* x, const intrusive_ptr<T>& y) {
  return x < y.get();
}

template <class T>
std::string to_string(const intrusive_ptr<T>& x) {
  auto v = reinterpret_cast<uintptr_t>(x.get());
  // we convert to hex representation, i.e.,
  // one byte takes two characters + null terminator + "0x" prefix
  char buf[sizeof(v) * 2 + 3];
  sprintf(buf, "%" PRIxPTR, v);
  return buf;
}

} // namespace caf

<|endoftext|>


<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <qimessaging/genericvalue.hpp>
#include <qimessaging/message.hpp>

#include <qimessaging/datastream.hpp>
#include <qi/log.hpp>
#include <qi/types.hpp>
#include <vector>
#include <cstring>
#include "src/buffer_p.hpp"

#if 0

#include <qimessaging/signature.hpp>

#define __QI_DEBUG_SERIALIZATION_DATA_R(x, d) {            \
  std::string sig = qi::signature< x >::value();           \
  std::cout << "read (" << sig << "): " << d << std::endl; \
}

#define __QI_DEBUG_SERIALIZATION_DATA_W(x, d) {            \
  std::string sig = qi::signature< x >::value();           \
  std::cout << "write(" << sig << "): " << d << std::endl; \
}
#else
# define __QI_DEBUG_SERIALIZATION_DATA_R(x, d)
# define __QI_DEBUG_SERIALIZATION_DATA_W(x, d)
#endif

namespace qi {

  template <typename T, typename T2, char S>
  static inline qi::IDataStream& deserialize(qi::IDataStream* ds, T &b)
  {
    T2 res;
    int ret = ds->read((void *)&res, sizeof(res));
    if (ret != sizeof(res))
      ds->setStatus(ds->Status_ReadPastEnd);
    __QI_DEBUG_SERIALIZATION_DATA_R(Type, b);
    b = res;
    return *ds;
  }

  template <typename T, typename T2, char S>
  static inline qi::ODataStream& serialize(qi::ODataStream* ds, T &b, bool inner)
  {
    T2 val = b;
    int ret = ds->write((const char*)&val, sizeof(val));
    if (!inner)
    {
      ds->getBuffer().signature() << S;
    }
    if (ret == -1)
      ds->setStatus(ds->Status_WriteError);
    __QI_DEBUG_SERIALIZATION_DATA_W(Type, b);
    return *ds;
  }

#define QI_SIMPLE_SERIALIZER_IMPL(Type, TypeCast, Signature)                   \
  IDataStream& IDataStream::operator>>(Type &b)                                \
  {                                                                            \
    return deserialize<Type, TypeCast, Signature>(this, b);                    \
  }                                                                            \
  ODataStream& ODataStream::operator<<(Type b)                                 \
  {                                                                            \
    return serialize<Type, TypeCast, Signature>(this, b, _innerSerialization); \
  }

  QI_SIMPLE_SERIALIZER_IMPL(bool, bool, 'b')
  QI_SIMPLE_SERIALIZER_IMPL(char, char, 'c')
  QI_SIMPLE_SERIALIZER_IMPL(signed char, signed char, 'c')
  QI_SIMPLE_SERIALIZER_IMPL(unsigned char, unsigned char, 'C')
  QI_SIMPLE_SERIALIZER_IMPL(short, short, 'w')
  QI_SIMPLE_SERIALIZER_IMPL(unsigned short, unsigned short, 'W')
  QI_SIMPLE_SERIALIZER_IMPL(int, int, 'i')
  QI_SIMPLE_SERIALIZER_IMPL(unsigned int, unsigned int, 'I')
  QI_SIMPLE_SERIALIZER_IMPL(long, qi::int64_t, 'l')
  QI_SIMPLE_SERIALIZER_IMPL(unsigned long, qi::uint64_t, 'L')
  QI_SIMPLE_SERIALIZER_IMPL(long long, long long, 'l')
  QI_SIMPLE_SERIALIZER_IMPL(unsigned long long, unsigned long long, 'L')
  QI_SIMPLE_SERIALIZER_IMPL(float, float, 'f')
  QI_SIMPLE_SERIALIZER_IMPL(double, double, 'd')

  IDataStream::IDataStream(const qi::Buffer& buffer)
  : _status(Status_Ok)
  , _reader(BufferReader(buffer))
  {
  }

  ODataStream::ODataStream(qi::Buffer &buffer)
  : _status(Status_Ok),
    _innerSerialization(0)
  {
    if (!buffer._p)
      buffer._p = boost::shared_ptr<BufferPrivate>(new BufferPrivate());
    _buffer = buffer;
    ++_buffer._p->nWriters;
  }

  ODataStream::~ODataStream()
  {
    --_buffer._p->nWriters;
  }
  IDataStream::~IDataStream()
  {
  }

  int ODataStream::write(const char *str, size_t len)
  {
    if (len) {
      if (!_innerSerialization)
      {
        getBuffer().signature() << 'r';
      }
      if (_buffer.write(str, len) < 0)
      {
        setStatus(Status_WriteError);
        __QI_DEBUG_SERIALIZATION_DATA_W(std::string, str);
        return -1;
      }
    }
    return len;
  }

  void ODataStream::writeString(const char *str, size_t len)
  {
    *this << (qi::uint32_t)len;
    if (len) {
      if (!_innerSerialization)
      {
        getBuffer().signature() << 's';
      }
      if (_buffer.write(str, len) != (int)len)
        setStatus(Status_WriteError);
      __QI_DEBUG_SERIALIZATION_DATA_W(std::string, str);
    }
  }

  // string
  IDataStream& IDataStream::operator>>(std::string &s)
  {
    qi::uint32_t sz = 0;
    *this >> sz;

    s.clear();
    if (sz) {
      char *data = static_cast<char *>(read(sz));
      if (!data) {
        qiLogError("datastream", "buffer empty");
        setStatus(Status_ReadPastEnd);
        return *this;
      }
      s.append(data, sz);
      __QI_DEBUG_SERIALIZATION_DATA_R(std::string, s);
    }

    return *this;
  }

  ODataStream& ODataStream::operator<<(const std::string &s)
  {
    writeString(s.c_str(), s.length());
    return *this;
  }

  ODataStream& ODataStream::operator<<(const char *s)
  {
    qi::uint32_t len = strlen(s);
    writeString(s, len);
    __QI_DEBUG_SERIALIZATION_DATA_W(char *, s);
    return *this;
  }

  ODataStream &ODataStream::operator<<(const qi::Buffer &meta) {
    if (!_innerSerialization)
    {
      getBuffer().signature() << "s";
    }

    ++_innerSerialization;

    *this << (uint32_t)meta.size();
    getBuffer().subBuffers().push_back(std::make_pair(getBuffer().size(), meta));

    --_innerSerialization;

    qiLogDebug("DataStream") << "Serializing buffer " << meta.size()
                             << " at " << getBuffer().size();
    return *this;
  }

  IDataStream &IDataStream::operator>>(qi::Buffer &meta) {
    BufferReader& reader = getBufferReader();
    uint32_t sz;
    *this >> sz;
    if (reader.hasSubBuffer())
    {
      meta = reader.getSubBuffer();
      if (meta.size() != sz)
        qiLogWarning("DataStream") << "Buffer size mismatch " << sz << " " << meta.size();
    }
    else
    {
      qiLogDebug("DataStream") << "Extracting buffer of size " << sz <<" at " << reader.position();
      meta.clear();
      void* ptr = meta.reserve(sz);
      memcpy(ptr, read(sz), sz);
    }
    return *this;
  }

  size_t IDataStream::read(void *data, size_t size)
  {
    return _reader.read(data, size);
  }

  void* IDataStream::read(size_t size)
  {
    return _reader.read(size);
  }

  IDataStream &IDataStream::operator>>(GenericValue &value)
  {
    std::string signature;
    *this >> signature;
    Type* type = 0; // Type::getCompatibleTypeWithSignature(signature);
    if (!type)
      qiLogError("qi.datastream") << "Could not find metatype for signature " << signature;
    else
    {
      value.type = type;
      value.value = 0; // value.type->deserialize(*this);
    }
    return *this;
  }

  ODataStream &ODataStream::operator<<(const GenericValue &value)
  {
    if (!_innerSerialization)
      getBuffer().signature() << "m";
    *this << value.signature();
    value.serialize(*this);
    return *this;
  }

  void ODataStream::beginList(uint32_t size, std::string elementSignature)
  {
    if (!_innerSerialization)
      getBuffer().signature() << "[" << elementSignature;
    ++_innerSerialization;
    *this << size;
  }

  void ODataStream::endList()
  {
    --_innerSerialization;
    if (!_innerSerialization)
      getBuffer().signature() << "]";
  }

  void ODataStream::beginMap(uint32_t size, std::string keySignature, std::string valueSignature)
  {
    if (!_innerSerialization)
      getBuffer().signature() << "{" << keySignature << valueSignature << "}";
    ++_innerSerialization;
     *this << size;
  }

  void ODataStream::endMap()
  {
    --_innerSerialization;
    if (!_innerSerialization)
      getBuffer().signature() << "}";
  }

  void ODataStream::beginTuple(std::string sig)
  {
    if (!_innerSerialization)
       getBuffer().signature() << "(" << sig << ")";
    ++_innerSerialization;
  }
  void ODataStream::endTuple()
  {
    --_innerSerialization;
  }
}

<commit_msg>DataStream: Fix signature generation.<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <qimessaging/genericvalue.hpp>
#include <qimessaging/message.hpp>

#include <qimessaging/datastream.hpp>
#include <qi/log.hpp>
#include <qi/types.hpp>
#include <vector>
#include <cstring>
#include "src/buffer_p.hpp"

#if 0

#include <qimessaging/signature.hpp>

#define __QI_DEBUG_SERIALIZATION_DATA_R(x, d) {            \
  std::string sig = qi::signature< x >::value();           \
  std::cout << "read (" << sig << "): " << d << std::endl; \
}

#define __QI_DEBUG_SERIALIZATION_DATA_W(x, d) {            \
  std::string sig = qi::signature< x >::value();           \
  std::cout << "write(" << sig << "): " << d << std::endl; \
}
#else
# define __QI_DEBUG_SERIALIZATION_DATA_R(x, d)
# define __QI_DEBUG_SERIALIZATION_DATA_W(x, d)
#endif

namespace qi {

  template <typename T, typename T2, char S>
  static inline qi::IDataStream& deserialize(qi::IDataStream* ds, T &b)
  {
    T2 res;
    int ret = ds->read((void *)&res, sizeof(res));
    if (ret != sizeof(res))
      ds->setStatus(ds->Status_ReadPastEnd);
    __QI_DEBUG_SERIALIZATION_DATA_R(Type, b);
    b = res;
    return *ds;
  }

  template <typename T, typename T2, char S>
  static inline qi::ODataStream& serialize(qi::ODataStream* ds, T &b, bool inner)
  {
    T2 val = b;
    int ret = ds->write((const char*)&val, sizeof(val));
    if (!inner)
    {
      ds->getBuffer().signature() << S;
    }
    if (ret == -1)
      ds->setStatus(ds->Status_WriteError);
    __QI_DEBUG_SERIALIZATION_DATA_W(Type, b);
    return *ds;
  }

#define QI_SIMPLE_SERIALIZER_IMPL(Type, TypeCast, Signature)                   \
  IDataStream& IDataStream::operator>>(Type &b)                                \
  {                                                                            \
    return deserialize<Type, TypeCast, Signature>(this, b);                    \
  }                                                                            \
  ODataStream& ODataStream::operator<<(Type b)                                 \
  {                                                                            \
    bool sig = _innerSerialization;                                            \
    ++_innerSerialization;                                                     \
    serialize<Type, TypeCast, Signature>(this, b, sig);                        \
    --_innerSerialization;                                                     \
    return *this;                                                              \
  }

  QI_SIMPLE_SERIALIZER_IMPL(bool, bool, 'b')
  QI_SIMPLE_SERIALIZER_IMPL(char, char, 'c')
  QI_SIMPLE_SERIALIZER_IMPL(signed char, signed char, 'c')
  QI_SIMPLE_SERIALIZER_IMPL(unsigned char, unsigned char, 'C')
  QI_SIMPLE_SERIALIZER_IMPL(short, short, 'w')
  QI_SIMPLE_SERIALIZER_IMPL(unsigned short, unsigned short, 'W')
  QI_SIMPLE_SERIALIZER_IMPL(int, int, 'i')
  QI_SIMPLE_SERIALIZER_IMPL(unsigned int, unsigned int, 'I')
  QI_SIMPLE_SERIALIZER_IMPL(long, qi::int64_t, 'l')
  QI_SIMPLE_SERIALIZER_IMPL(unsigned long, qi::uint64_t, 'L')
  QI_SIMPLE_SERIALIZER_IMPL(long long, long long, 'l')
  QI_SIMPLE_SERIALIZER_IMPL(unsigned long long, unsigned long long, 'L')
  QI_SIMPLE_SERIALIZER_IMPL(float, float, 'f')
  QI_SIMPLE_SERIALIZER_IMPL(double, double, 'd')

  IDataStream::IDataStream(const qi::Buffer& buffer)
  : _status(Status_Ok)
  , _reader(BufferReader(buffer))
  {
  }

  ODataStream::ODataStream(qi::Buffer &buffer)
  : _status(Status_Ok),
    _innerSerialization(0)
  {
    if (!buffer._p)
      buffer._p = boost::shared_ptr<BufferPrivate>(new BufferPrivate());
    _buffer = buffer;
    ++_buffer._p->nWriters;
  }

  ODataStream::~ODataStream()
  {
    --_buffer._p->nWriters;
  }
  IDataStream::~IDataStream()
  {
  }

  int ODataStream::write(const char *str, size_t len)
  {
    if (len) {
      if (!_innerSerialization)
      {
        getBuffer().signature() << 's';
      }
      if (_buffer.write(str, len) < 0)
      {
        setStatus(Status_WriteError);
        __QI_DEBUG_SERIALIZATION_DATA_W(std::string, str);
        return -1;
      }
    }
    return len;
  }

  void ODataStream::writeString(const char *str, size_t len)
  {
    ++_innerSerialization;
    *this << (qi::uint32_t)len;
    --_innerSerialization;
    if (len) {
      if (!_innerSerialization)
      {
        getBuffer().signature() << 's';
      }
      if (_buffer.write(str, len) != (int)len)
        setStatus(Status_WriteError);
      __QI_DEBUG_SERIALIZATION_DATA_W(std::string, str);
    }
  }

  // string
  IDataStream& IDataStream::operator>>(std::string &s)
  {
    qi::uint32_t sz = 0;
    *this >> sz;

    s.clear();
    if (sz) {
      char *data = static_cast<char *>(read(sz));
      if (!data) {
        qiLogError("datastream", "buffer empty");
        setStatus(Status_ReadPastEnd);
        return *this;
      }
      s.append(data, sz);
      __QI_DEBUG_SERIALIZATION_DATA_R(std::string, s);
    }

    return *this;
  }

  ODataStream& ODataStream::operator<<(const std::string &s)
  {
    writeString(s.c_str(), s.length());
    return *this;
  }

  ODataStream& ODataStream::operator<<(const char *s)
  {
    qi::uint32_t len = strlen(s);
    writeString(s, len);
    __QI_DEBUG_SERIALIZATION_DATA_W(char *, s);
    return *this;
  }

  ODataStream &ODataStream::operator<<(const qi::Buffer &meta) {
    if (!_innerSerialization)
    {
      getBuffer().signature() << "s";
    }

    ++_innerSerialization;

    *this << (uint32_t)meta.size();
    getBuffer().subBuffers().push_back(std::make_pair(getBuffer().size(), meta));

    --_innerSerialization;

    qiLogDebug("DataStream") << "Serializing buffer " << meta.size()
                             << " at " << getBuffer().size();
    return *this;
  }

  IDataStream &IDataStream::operator>>(qi::Buffer &meta) {
    BufferReader& reader = getBufferReader();
    uint32_t sz;
    *this >> sz;
    if (reader.hasSubBuffer())
    {
      meta = reader.getSubBuffer();
      if (meta.size() != sz)
        qiLogWarning("DataStream") << "Buffer size mismatch " << sz << " " << meta.size();
    }
    else
    {
      qiLogDebug("DataStream") << "Extracting buffer of size " << sz <<" at " << reader.position();
      meta.clear();
      void* ptr = meta.reserve(sz);
      memcpy(ptr, read(sz), sz);
    }
    return *this;
  }

  size_t IDataStream::read(void *data, size_t size)
  {
    return _reader.read(data, size);
  }

  void* IDataStream::read(size_t size)
  {
    return _reader.read(size);
  }

  IDataStream &IDataStream::operator>>(GenericValue &value)
  {
    std::string signature;
    *this >> signature;
    Type* type = 0; // Type::getCompatibleTypeWithSignature(signature);
    if (!type)
      qiLogError("qi.datastream") << "Could not find metatype for signature " << signature;
    else
    {
      value.type = type;
      value.value = 0; // value.type->deserialize(*this);
    }
    return *this;
  }

  ODataStream &ODataStream::operator<<(const GenericValue &value)
  {
    if (!_innerSerialization)
      getBuffer().signature() << "m";
    ++_innerSerialization;
    *this << value.signature();
    value.serialize(*this);
    --_innerSerialization;
    return *this;
  }

  void ODataStream::beginList(uint32_t size, std::string elementSignature)
  {
    if (!_innerSerialization)
      getBuffer().signature() << "[" << elementSignature;
    ++_innerSerialization;
    *this << size;
  }

  void ODataStream::endList()
  {
    --_innerSerialization;
    if (!_innerSerialization)
      getBuffer().signature() << "]";
  }

  void ODataStream::beginMap(uint32_t size, std::string keySignature, std::string valueSignature)
  {
    if (!_innerSerialization)
      getBuffer().signature() << "{" << keySignature << valueSignature << "}";
    ++_innerSerialization;
     *this << size;
  }

  void ODataStream::endMap()
  {
    --_innerSerialization;
    if (!_innerSerialization)
      getBuffer().signature() << "}";
  }

  void ODataStream::beginTuple(std::string sig)
  {
    if (!_innerSerialization)
       getBuffer().signature() << "(" << sig << ")";
    ++_innerSerialization;
  }
  void ODataStream::endTuple()
  {
    --_innerSerialization;
  }
}

<|endoftext|>


<commit_before>#include <memory>
#include <iostream>
#include <sstream>
#include <gtest/gtest.h>
#include <Zend/zend.h>
#include "phpcxx/extension.h"
#include "testsapi.h"

class MyExtension : public phpcxx::Extension {
public:
    int module_startup_called   = 0;
    int module_shutdown_called  = 0;
    int request_startup_called  = 0;
    int request_shutdown_called = 0;

    using phpcxx::Extension::Extension;

protected:
    virtual void onModuleStartup() override
    {
        ++this->module_startup_called;
    }

    virtual void onModuleShutdown() override
    {
        ++this->module_shutdown_called;
    }

    virtual void onRequestStartup() override
    {
        ++this->request_startup_called;
    }

    virtual void onRequestShutdown() override
    {
        ++this->request_shutdown_called;
    }

};

TEST(LifecycleTest, NormalRequest)
{
    MyExtension ext("LifeCycle", "0.0");

    EXPECT_EQ(0, ext.module_startup_called);
    EXPECT_EQ(0, ext.module_shutdown_called);
    EXPECT_EQ(0, ext.request_startup_called);
    EXPECT_EQ(0, ext.request_shutdown_called);

    {
        TestSAPI sapi(std::cout, std::cerr);
        sapi.addExtension(ext);

        sapi.initialize();
        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(0, ext.request_startup_called);
        EXPECT_EQ(0, ext.request_shutdown_called);

        sapi.run([&ext]() {
            EXPECT_EQ(1, ext.module_startup_called);
            EXPECT_EQ(0, ext.module_shutdown_called);
            EXPECT_EQ(1, ext.request_startup_called);
            EXPECT_EQ(0, ext.request_shutdown_called);
        });

        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(1, ext.request_startup_called);
        EXPECT_EQ(1, ext.request_shutdown_called);
    }

    EXPECT_EQ(1, ext.module_startup_called);
    EXPECT_EQ(1, ext.module_shutdown_called);
    EXPECT_EQ(1, ext.request_startup_called);
    EXPECT_EQ(1, ext.request_shutdown_called);
}

TEST(LifecycleTest, ErroredRequest)
{
    MyExtension ext("LifeCycle", "0.0");

    EXPECT_EQ(0, ext.module_startup_called);
    EXPECT_EQ(0, ext.module_shutdown_called);
    EXPECT_EQ(0, ext.request_startup_called);
    EXPECT_EQ(0, ext.request_shutdown_called);

    std::stringstream out;
    std::stringstream err;

    {
        TestSAPI sapi(out, err);
        sapi.addExtension(ext);

        sapi.initialize();
        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(0, ext.request_startup_called);
        EXPECT_EQ(0, ext.request_shutdown_called);

        sapi.run([&ext]() {
            EXPECT_EQ(1, ext.module_startup_called);
            EXPECT_EQ(0, ext.module_shutdown_called);
            EXPECT_EQ(1, ext.request_startup_called);
            EXPECT_EQ(0, ext.request_shutdown_called);

            zend_error(E_ERROR, "Very fatal error");
        });

        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(1, ext.request_startup_called);
        EXPECT_EQ(1, ext.request_shutdown_called);

        EXPECT_EQ(out.str(), "");
        std::string errors = err.str();
        EXPECT_NE(errors.find("Very fatal error"), std::string::npos);
    }

    EXPECT_EQ(1, ext.module_startup_called);
    EXPECT_EQ(1, ext.module_shutdown_called);
    EXPECT_EQ(1, ext.request_startup_called);
    EXPECT_EQ(1, ext.request_shutdown_called);
}

TEST(LifecycleTest, MultipleRequests)
{
    MyExtension ext("LifeCycle", "0.0");

    EXPECT_EQ(0, ext.module_startup_called);
    EXPECT_EQ(0, ext.module_shutdown_called);
    EXPECT_EQ(0, ext.request_startup_called);
    EXPECT_EQ(0, ext.request_shutdown_called);
    int n = 100;

    {
        TestSAPI sapi(std::cout, std::cerr);
        sapi.addExtension(ext);

        sapi.initialize();
        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(0, ext.request_startup_called);
        EXPECT_EQ(0, ext.request_shutdown_called);

        for (int i=0; i<n; ++i) {
            sapi.run([&ext, &i]() {
                EXPECT_EQ(1,   ext.module_startup_called);
                EXPECT_EQ(0,   ext.module_shutdown_called);
                EXPECT_EQ(i+1, ext.request_startup_called);
                EXPECT_EQ(i,   ext.request_shutdown_called);
            });
        }

        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(n, ext.request_startup_called);
        EXPECT_EQ(n, ext.request_shutdown_called);
    }

    EXPECT_EQ(1, ext.module_startup_called);
    EXPECT_EQ(1, ext.module_shutdown_called);
    EXPECT_EQ(n, ext.request_startup_called);
    EXPECT_EQ(n, ext.request_shutdown_called);
}
<commit_msg>More tests<commit_after>#include <memory>
#include <iostream>
#include <sstream>
#include <gtest/gtest.h>
#include <Zend/zend.h>
#include "phpcxx/extension.h"
#include "testsapi.h"

class MyExtension : public phpcxx::Extension {
public:
    int module_startup_called   = 0;
    int module_shutdown_called  = 0;
    int request_startup_called  = 0;
    int request_shutdown_called = 0;

    using phpcxx::Extension::Extension;

protected:
    virtual void onModuleStartup() override
    {
        ++this->module_startup_called;
    }

    virtual void onModuleShutdown() override
    {
        ++this->module_shutdown_called;
    }

    virtual void onRequestStartup() override
    {
        ++this->request_startup_called;
    }

    virtual void onRequestShutdown() override
    {
        ++this->request_shutdown_called;
    }
};

class LoadOtherExtension : public phpcxx::Extension {
public:
    int module_startup_called   = 0;
    int module_shutdown_called  = 0;
    int request_startup_called  = 0;
    int request_shutdown_called = 0;

    LoadOtherExtension(const char* name, const char* version, phpcxx::Extension& other)
        : phpcxx::Extension(name, version), m_other(other)
    {
    }

protected:
    virtual void onModuleStartup() override
    {
        ++this->module_startup_called;
        this->registerExtension(this->m_other);
    }

    virtual void onModuleShutdown() override
    {
        ++this->module_shutdown_called;
    }

    virtual void onRequestStartup() override
    {
        ++this->request_startup_called;
    }

    virtual void onRequestShutdown() override
    {
        ++this->request_shutdown_called;
    }

private:
    phpcxx::Extension& m_other;
};

TEST(LifecycleTest, NormalRequest)
{
    MyExtension ext("LifeCycle", "0.0");

    EXPECT_EQ(0, ext.module_startup_called);
    EXPECT_EQ(0, ext.module_shutdown_called);
    EXPECT_EQ(0, ext.request_startup_called);
    EXPECT_EQ(0, ext.request_shutdown_called);

    {
        TestSAPI sapi(std::cout, std::cerr);
        sapi.addExtension(ext);

        sapi.initialize();
        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(0, ext.request_startup_called);
        EXPECT_EQ(0, ext.request_shutdown_called);

        sapi.run([&ext]() {
            EXPECT_EQ(1, ext.module_startup_called);
            EXPECT_EQ(0, ext.module_shutdown_called);
            EXPECT_EQ(1, ext.request_startup_called);
            EXPECT_EQ(0, ext.request_shutdown_called);
        });

        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(1, ext.request_startup_called);
        EXPECT_EQ(1, ext.request_shutdown_called);
    }

    EXPECT_EQ(1, ext.module_startup_called);
    EXPECT_EQ(1, ext.module_shutdown_called);
    EXPECT_EQ(1, ext.request_startup_called);
    EXPECT_EQ(1, ext.request_shutdown_called);
}

TEST(LifecycleTest, ErroredRequest)
{
    MyExtension ext("LifeCycle", "0.0");

    EXPECT_EQ(0, ext.module_startup_called);
    EXPECT_EQ(0, ext.module_shutdown_called);
    EXPECT_EQ(0, ext.request_startup_called);
    EXPECT_EQ(0, ext.request_shutdown_called);

    std::stringstream out;
    std::stringstream err;

    {
        TestSAPI sapi(out, err);
        sapi.addExtension(ext);

        sapi.initialize();
        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(0, ext.request_startup_called);
        EXPECT_EQ(0, ext.request_shutdown_called);

        sapi.run([&ext]() {
            EXPECT_EQ(1, ext.module_startup_called);
            EXPECT_EQ(0, ext.module_shutdown_called);
            EXPECT_EQ(1, ext.request_startup_called);
            EXPECT_EQ(0, ext.request_shutdown_called);

            zend_error(E_ERROR, "Very fatal error");
        });

        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(1, ext.request_startup_called);
        EXPECT_EQ(1, ext.request_shutdown_called);

        EXPECT_EQ(out.str(), "");
        std::string errors = err.str();
        EXPECT_NE(errors.find("Very fatal error"), std::string::npos);
    }

    EXPECT_EQ(1, ext.module_startup_called);
    EXPECT_EQ(1, ext.module_shutdown_called);
    EXPECT_EQ(1, ext.request_startup_called);
    EXPECT_EQ(1, ext.request_shutdown_called);
}

TEST(LifecycleTest, MultipleRequests)
{
    MyExtension ext("LifeCycle", "0.0");

    EXPECT_EQ(0, ext.module_startup_called);
    EXPECT_EQ(0, ext.module_shutdown_called);
    EXPECT_EQ(0, ext.request_startup_called);
    EXPECT_EQ(0, ext.request_shutdown_called);
    int n = 100;

    {
        TestSAPI sapi(std::cout, std::cerr);
        sapi.addExtension(ext);

        sapi.initialize();
        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(0, ext.request_startup_called);
        EXPECT_EQ(0, ext.request_shutdown_called);

        for (int i=0; i<n; ++i) {
            sapi.run([&ext, &i]() {
                EXPECT_EQ(1,   ext.module_startup_called);
                EXPECT_EQ(0,   ext.module_shutdown_called);
                EXPECT_EQ(i+1, ext.request_startup_called);
                EXPECT_EQ(i,   ext.request_shutdown_called);
            });
        }

        EXPECT_EQ(1, ext.module_startup_called);
        EXPECT_EQ(0, ext.module_shutdown_called);
        EXPECT_EQ(n, ext.request_startup_called);
        EXPECT_EQ(n, ext.request_shutdown_called);
    }

    EXPECT_EQ(1, ext.module_startup_called);
    EXPECT_EQ(1, ext.module_shutdown_called);
    EXPECT_EQ(n, ext.request_startup_called);
    EXPECT_EQ(n, ext.request_shutdown_called);
}

TEST(LifecycleTest, AdditionalModule)
{
    {
        MyExtension ext2("LifeCycle2", "0.0");
        LoadOtherExtension ext1("LifeCycle1", "0.0", ext2);

        {
            TestSAPI sapi(std::cout, std::cerr);
            sapi.addExtension(ext1);

            sapi.initialize();
            EXPECT_EQ(1, ext1.module_startup_called);
            EXPECT_EQ(1, ext2.module_startup_called);

            sapi.run([&ext1, &ext2]() {
                EXPECT_EQ(1, ext1.request_startup_called);
                EXPECT_EQ(1, ext2.request_startup_called);
            });
        }

        EXPECT_EQ(1, ext1.module_shutdown_called);
        EXPECT_EQ(1, ext2.module_shutdown_called);
    }

    {
        MyExtension ext1("LifeCycle1", "0.0");
        MyExtension ext2("LifeCycle2", "0.0");

        {
            TestSAPI sapi(std::cout, std::cerr);
            sapi.addExtension(ext1);

            sapi.initialize();
            ext1.registerExtension(ext2);
            EXPECT_EQ(1, ext1.module_startup_called);
            EXPECT_EQ(1, ext2.module_startup_called);

            sapi.run([&ext1, &ext2]() {
                EXPECT_EQ(1, ext1.request_startup_called);
                // When a module is loaded after MINIT phase,
                // its request startup / shutdown functions
                // do not get registered
                // However, I would prefer not to rely upon
                // this behavior
                // EXPECT_EQ(0, ext2.request_startup_called);
            });

            sapi.run([&ext1, &ext2]() {
                EXPECT_EQ(2, ext1.request_startup_called);
                // See above
                // EXPECT_EQ(0, ext2.request_startup_called);
            });
        }

        EXPECT_EQ(1, ext1.module_shutdown_called);
        EXPECT_EQ(1, ext2.module_shutdown_called);
    }
}
<|endoftext|>