<commit_before>/**
  * Touhou Community Reliant Automatic Patcher
  * Main DLL
  *
  * ----
  *
  * Logging functions.
  */

#include "thcrap.h"
#include <io.h>
#include <fcntl.h>
#include <ThreadPool.h>

// -------
// Globals
// -------
static HANDLE log_file = INVALID_HANDLE_VALUE;
static bool console_open = false;
static ThreadPool *log_queue = NULL;
// For checking nested thcrap instances that access the same log file.
// We only want to print an error message for the first instance.
static HANDLE log_filemapping = INVALID_HANDLE_VALUE;
static const char LOG[] = "logs/thcrap_log.txt";
static const char LOG_ROTATED[] = "logs/thcrap_log.%d.txt";
static const int ROTATIONS = 5; // Number of backups to keep
static void (*log_print_hook)(const char*) = NULL;
static void(*log_nprint_hook)(const char*, size_t) = NULL;
static HWND mbox_owner_hwnd = NULL; // Set by log_mbox_set_owner
// -----------------------

struct lasterror_t {
	char str[DECIMAL_DIGITS_BOUND(DWORD) + 1];
};

THREAD_LOCAL(lasterror_t, lasterror_tls, nullptr, nullptr);

const char* lasterror_str_for(DWORD err)
{
	switch(err) {
	case ERROR_SHARING_VIOLATION:
		return "File in use";
	case ERROR_MOD_NOT_FOUND:
		return "File not found";
	default: // -Wswitch...
		break;
	}
	auto str = lasterror_tls_get();
	if(!str) {
		static lasterror_t lasterror_static;
		str = &lasterror_static;
	}
	snprintf(str->str, sizeof(str->str), "%lu", err);
	return str->str;
}

const char* lasterror_str()
{
	return lasterror_str_for(GetLastError());
}

void log_set_hook(void(*print_hook)(const char*), void(*nprint_hook)(const char*,size_t)){
	log_print_hook = print_hook;
	log_nprint_hook = nprint_hook;
}

// Rotation
// --------
void log_fn_for_rotation(char *fn, int rotnum)
{
	if(rotnum == 0) {
		strcpy(fn, LOG);
	} else {
		sprintf(fn, LOG_ROTATED, rotnum);
	}
}

void log_rotate(void)
{
	size_t rot_fn_len = MAX(sizeof(LOG_ROTATED), sizeof(LOG));
	VLA(char, rot_from, rot_fn_len);
	VLA(char, rot_to, rot_fn_len);

	for(int rotation = ROTATIONS; rotation > 0; rotation--) {
		log_fn_for_rotation(rot_from, rotation - 1);
		log_fn_for_rotation(rot_to, rotation);
		MoveFileExU(rot_from, rot_to, MOVEFILE_REPLACE_EXISTING);
	}

	VLA_FREE(rot_from);
	VLA_FREE(rot_to);
}
// --------

void log_print(const char *str)
{
	if (log_queue) {
		log_queue->enqueue([str = strdup(str)]() {
			DWORD byteRet;
			if (console_open) {
				WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), str, strlen(str), &byteRet, NULL);
			}
			if (log_file) {
				WriteFile(log_file, str, strlen(str), &byteRet, NULL);
			}
			if (log_print_hook) {
				log_print_hook(str);
			}
			free(str);
		});
	}
}

void log_print_fast(const char* str, size_t n) {
	if (log_queue) {
		log_queue->enqueue([str = strndup(str, n), n]() {
			DWORD byteRet;
			if (console_open) {
				WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), str, n, &byteRet, NULL);
			}
			if (log_file != INVALID_HANDLE_VALUE) {
				WriteFile(log_file, str, n, &byteRet, NULL);
			}
			if (log_print_hook) {
				log_print_hook(str);
			}
			free(str);
		});
	}
}

void log_nprint(const char *str, size_t n)
{
	if (log_queue) {
		log_queue->enqueue([str = strndup(str, n), n]() {
			DWORD byteRet;
			if (console_open) {
				WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), str, n, &byteRet, NULL);
			}
			if (log_file != INVALID_HANDLE_VALUE) {
				WriteFile(log_file, str, n, &byteRet, NULL);
			}
			if (log_nprint_hook) {
				log_nprint_hook(str, n);
			}
			free(str);
		});
	}
}

void log_vprintf(const char *format, va_list va)
{
	va_list va2;
	va_copy(va2, va);
	const int total_size = vsnprintf(NULL, 0, format, va2);
	va_end(va2);
	if (total_size > 0) {
		VLA(char, str, total_size + 1);
		vsprintf(str, format, va);
		log_print_fast(str, total_size);
		VLA_FREE(str);
	}
}

void log_printf(const char *format, ...)
{
	va_list va;
	va_start(va, format);
	log_vprintf(format, va);
	va_end(va);
}

/**
  * Message box functions.
  */

struct EnumStatus
{
	HWND hwnd;
	int w;
	int h;
};

static BOOL CALLBACK enumWindowProc(HWND hwnd, LPARAM lParam)
{
	EnumStatus *status = (EnumStatus*)lParam;

	if (!IsWindowVisible(hwnd)) {
		return TRUE;
	}

	DWORD pid;
	GetWindowThreadProcessId(hwnd, &pid);
	if (pid != GetCurrentProcessId()) {
		return TRUE;
	}

	RECT rect;
	GetWindowRect(hwnd, &rect);
	int w = rect.right - rect.left;
	int h = rect.bottom - rect.top;
	if (w * h > status->w * status->h) {
		status->hwnd = hwnd;
	}

	return TRUE;
}

static HWND guess_mbox_owner()
{
	// If an owner have been set, easy - just return it.
	if (mbox_owner_hwnd) {
		return mbox_owner_hwnd;
	}

	// Time to guess. If the current thread has an active window, it's probably a good window to steal.
	HWND hwnd = GetActiveWindow();
	if (hwnd) {
		return hwnd;
	}

	// It's getting harder. Look at all the top-level visible windows of our processes, and take the biggest one.
	EnumStatus status;
	status.hwnd = nullptr;
	status.w = 10; // Ignore windows smaller than 10x10
	status.h = 10;
	EnumWindows(enumWindowProc, (LPARAM)&status);
	if (status.hwnd) {
		return status.hwnd;
	}

	// Let's hope our process is allowed to take the focus.
	return nullptr;
}

int log_mbox(const char *caption, const UINT type, const char *text)
{
	log_printf(
		"---------------------------\n"
		"%s\n"
		"---------------------------\n"
		, text
	);
	return MessageBox(guess_mbox_owner(), text, (caption ? caption : PROJECT_NAME), type);
}

int log_vmboxf(const char *caption, const UINT type, const char *format, va_list va)
{
	int ret = 0;
	if(format) {
		va_list va2;
		va_copy(va2, va);
		const int total_size = vsnprintf(NULL, 0, format, va2);
		va_end(va2);
		if (total_size > 0) {
			VLA(char, formatted_str, total_size + 1);
			vsprintf(formatted_str, format, va);
			ret = log_mbox(caption, type, formatted_str);
			VLA_FREE(formatted_str);
		}
	}
	return ret;
}

int log_mboxf(const char *caption, const UINT type, const char *format, ...)
{
	va_list va;
	va_start(va, format);
	int ret = log_vmboxf(caption, type, format, va);
	va_end(va);
	return ret;
}

void log_mbox_set_owner(HWND hwnd)
{
	mbox_owner_hwnd = hwnd;
}

static void OpenConsole(void)
{
	if(console_open) {
		return;
	}
	AllocConsole();

	// To match the behavior of the native Windows console, Wine additionally
	// needs read rights because its WriteConsole() implementation calls
	// GetConsoleMode(), and setvbuf() because… I don't know?

	/// This breaks all normal, unlogged printf() calls to stdout!
	// _setmode(_fileno(stdout), _O_U16TEXT);

	console_open = true;
}

/// Per-module loggers
/// ------------------
std::nullptr_t logger_t::verrorf(const char *format, va_list va) const
{
	va_list va2;
	va_copy(va2, va);
	const int total_size = vsnprintf(NULL, 0, format, va2);
	va_end(va2);
	if (total_size > 0) {
		VLA(char, formatted_str, total_size + 1 + prefix.length());
		memcpy(formatted_str, prefix.data(), prefix.length());
		vsprintf(formatted_str + prefix.length(), format, va);
		log_mbox(err_caption, MB_OK | MB_ICONERROR, formatted_str);
		VLA_FREE(formatted_str);
	}
	return nullptr;
}

std::nullptr_t logger_t::errorf(const char *format, ...) const
{
	va_list va;
	va_start(va, format);
	auto ret = verrorf(format, va);
	va_end(va);
	return ret;
}
/// ------------------

void log_init(int console)
{
	CreateDirectoryU("logs", NULL);
	log_rotate();

	// Using CreateFile, _open_osfhandle and _fdopen instead of plain fopen because we need the flag FILE_SHARE_DELETE for log rotation
	log_file = CreateFileU(LOG, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
	log_queue = new ThreadPool(1);
#ifdef _DEBUG
	OpenConsole();
#else
	if(log_file) {

		constexpr std::string_view DashUChar = u8"―";

		const size_t line_len = (strlen(PROJECT_NAME) + strlen(" logfile")) * DashUChar.length();
		VLA(char, line, line_len + 1);
		line[line_len] = '\0';

		for (size_t i = 0; i < line_len; i += DashUChar.length()) {
			memcpy(&line[i], DashUChar.data(), DashUChar.length());
		}

		log_printf("%s\n", line);
		log_printf("%s logfile\n", PROJECT_NAME);
		log_printf("Branch: %s\n", PROJECT_BRANCH);
		log_printf("Version: %s\n", PROJECT_VERSION_STRING);
		log_printf("Build time: "  __DATE__ " " __TIME__ "\n");
#if defined(BUILDER_NAME_W)
		{
			const wchar_t *builder = BUILDER_NAME_W;
			UTF8_DEC(builder);
			UTF8_CONV(builder);
			log_printf("Built by: %s\n", builder_utf8);
			UTF8_FREE(builder);
		}
#elif defined(BUILDER_NAME)
		log_printf("Built by: %s\n", BUILDER_NAME);
#endif
		log_printf("Command line: %s\n", GetCommandLineU());
		log_printf("%s\n\n", line);
		FlushFileBuffers(log_file);
		VLA_FREE(line);
	}
	if (console) {
		OpenConsole();
	}
#endif
	size_t cur_dir_len = GetCurrentDirectoryU(0, nullptr);
	size_t full_fn_len = cur_dir_len + sizeof(LOG);
	VLA(char, full_fn, full_fn_len);
	defer(VLA_FREE(full_fn));
	GetCurrentDirectoryU(cur_dir_len, full_fn);
	full_fn[cur_dir_len - 1] = '/';
	full_fn[cur_dir_len] = '\0';
	str_slash_normalize(full_fn); // Necessary!
	memcpy(full_fn + cur_dir_len, LOG, sizeof(LOG));

	log_filemapping = CreateFileMappingU(
		INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, 1, full_fn
	);
	if(log_file == INVALID_HANDLE_VALUE && GetLastError() != ERROR_ALREADY_EXISTS) {
		auto ret = log_mboxf(nullptr, MB_OKCANCEL | MB_ICONHAND,
			"Error creating %s: %s\n"
			"\n"
			"Logging will be unavailable. "
			"Further writes to this directory are likely to fail as well. "
			"Moving %s to a different directory will probably fix this.\n"
			"\n"
			"Continue?",
			full_fn, strerror(errno), PROJECT_NAME_SHORT
		);
		if(ret == IDCANCEL) {
			auto pExitProcess = ((void (TH_STDCALL*)(UINT))detour_top(
				"kernel32.dll", "ExitProcess", (FARPROC)thcrap_ExitProcess
			));
			pExitProcess(-1);
		}
	}
}

void log_exit(void)
{
	// Run the destructor to ensure all remaining log messages were printed
	delete log_queue;

	if(console_open)
		FreeConsole();
	if(log_file) {
		CloseHandle(log_filemapping);
		CloseHandle(log_file);
		log_file = INVALID_HANDLE_VALUE;
	}
}
<commit_msg>Revert "thcrap: fix log build errors"<commit_after>/**
  * Touhou Community Reliant Automatic Patcher
  * Main DLL
  *
  * ----
  *
  * Logging functions.
  */

#include "thcrap.h"
#include <io.h>
#include <fcntl.h>
#include <ThreadPool.h>

// -------
// Globals
// -------
static HANDLE log_file = INVALID_HANDLE_VALUE;
static bool console_open = false;
static ThreadPool *log_queue = NULL;
// For checking nested thcrap instances that access the same log file.
// We only want to print an error message for the first instance.
static HANDLE log_filemapping = INVALID_HANDLE_VALUE;
static const char LOG[] = "logs/thcrap_log.txt";
static const char LOG_ROTATED[] = "logs/thcrap_log.%d.txt";
static const int ROTATIONS = 5; // Number of backups to keep
static void (*log_print_hook)(const char*) = NULL;
static void(*log_nprint_hook)(const char*, size_t) = NULL;
static HWND mbox_owner_hwnd = NULL; // Set by log_mbox_set_owner
// -----------------------

struct lasterror_t {
	char str[DECIMAL_DIGITS_BOUND(DWORD) + 1];
};

THREAD_LOCAL(lasterror_t, lasterror_tls, nullptr, nullptr);

const char* lasterror_str_for(DWORD err)
{
	switch(err) {
	case ERROR_SHARING_VIOLATION:
		return "File in use";
	case ERROR_MOD_NOT_FOUND:
		return "File not found";
	default: // -Wswitch...
		break;
	}
	auto str = lasterror_tls_get();
	if(!str) {
		static lasterror_t lasterror_static;
		str = &lasterror_static;
	}
	snprintf(str->str, sizeof(str->str), "%lu", err);
	return str->str;
}

const char* lasterror_str()
{
	return lasterror_str_for(GetLastError());
}

void log_set_hook(void(*print_hook)(const char*), void(*nprint_hook)(const char*,size_t)){
	log_print_hook = print_hook;
	log_nprint_hook = nprint_hook;
}

// Rotation
// --------
void log_fn_for_rotation(char *fn, int rotnum)
{
	if(rotnum == 0) {
		strcpy(fn, LOG);
	} else {
		sprintf(fn, LOG_ROTATED, rotnum);
	}
}

void log_rotate(void)
{
	size_t rot_fn_len = MAX(sizeof(LOG_ROTATED), sizeof(LOG));
	VLA(char, rot_from, rot_fn_len);
	VLA(char, rot_to, rot_fn_len);

	for(int rotation = ROTATIONS; rotation > 0; rotation--) {
		log_fn_for_rotation(rot_from, rotation - 1);
		log_fn_for_rotation(rot_to, rotation);
		MoveFileExU(rot_from, rot_to, MOVEFILE_REPLACE_EXISTING);
	}

	VLA_FREE(rot_from);
	VLA_FREE(rot_to);
}
// --------

void log_print(const char *str)
{
	if (log_queue) {
		log_queue->enqueue([str = strdup(str)]() {
			DWORD byteRet;
			if (console_open) {
				WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), str, strlen(str), &byteRet, NULL);
			}
			if (log_file) {
				WriteFile(log_file, str, strlen(str), &byteRet, NULL);
			}
			if (log_print_hook) {
				log_print_hook(str);
			}
			free(str);
		});
	}
}

void log_print_fast(const char* str, size_t n) {
	if (log_queue) {
		log_queue->enqueue([str = strndup(str, n), n]() {
			DWORD byteRet;
			if (console_open) {
				WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), str, n, &byteRet, NULL);
			}
			if (log_file != INVALID_HANDLE_VALUE) {
				WriteFile(log_file, str, n, &byteRet, NULL);
			}
			if (log_print_hook) {
				log_print_hook(str);
			}
			free(str);
		});
	}
}

void log_nprint(const char *str, size_t n)
{
	if (log_queue) {
		log_queue->enqueue([str = strndup(str, n), n]() {
			DWORD byteRet;
			if (console_open) {
				WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), str, n, &byteRet, NULL);
			}
			if (log_file != INVALID_HANDLE_VALUE) {
				WriteFile(log_file, str, n, &byteRet, NULL);
			}
			if (log_nprint_hook) {
				log_nprint_hook(str, n);
			}
			free(str);
		});
	}
}

void log_vprintf(const char *format, va_list va)
{
	va_list va2;
	va_copy(va2, va);
	const int total_size = vsnprintf(NULL, 0, format, va2);
	va_end(va2);
	if (total_size > 0) {
		VLA(char, str, total_size + 1);
		vsprintf(str, format, va);
		log_print_fast(str, total_size);
		VLA_FREE(str);
	}
}

void log_printf(const char *format, ...)
{
	va_list va;
	va_start(va, format);
	log_vprintf(format, va);
	va_end(va);
}

/**
  * Message box functions.
  */

struct EnumStatus
{
	HWND hwnd;
	int w;
	int h;
};

static BOOL CALLBACK enumWindowProc(HWND hwnd, LPARAM lParam)
{
	EnumStatus *status = (EnumStatus*)lParam;

	if (!IsWindowVisible(hwnd)) {
		return TRUE;
	}

	DWORD pid;
	GetWindowThreadProcessId(hwnd, &pid);
	if (pid != GetCurrentProcessId()) {
		return TRUE;
	}

	RECT rect;
	GetWindowRect(hwnd, &rect);
	int w = rect.right - rect.left;
	int h = rect.bottom - rect.top;
	if (w * h > status->w * status->h) {
		status->hwnd = hwnd;
	}

	return TRUE;
}

static HWND guess_mbox_owner()
{
	// If an owner have been set, easy - just return it.
	if (mbox_owner_hwnd) {
		return mbox_owner_hwnd;
	}

	// Time to guess. If the current thread has an active window, it's probably a good window to steal.
	HWND hwnd = GetActiveWindow();
	if (hwnd) {
		return hwnd;
	}

	// It's getting harder. Look at all the top-level visible windows of our processes, and take the biggest one.
	EnumStatus status;
	status.hwnd = nullptr;
	status.w = 10; // Ignore windows smaller than 10x10
	status.h = 10;
	EnumWindows(enumWindowProc, (LPARAM)&status);
	if (status.hwnd) {
		return status.hwnd;
	}

	// Let's hope our process is allowed to take the focus.
	return nullptr;
}

int log_mbox(const char *caption, const UINT type, const char *text)
{
	log_printf(
		"---------------------------\n"
		"%s\n"
		"---------------------------\n"
		, text
	);
	return MessageBox(guess_mbox_owner(), text, (caption ? caption : PROJECT_NAME), type);
}

int log_vmboxf(const char *caption, const UINT type, const char *format, va_list va)
{
	int ret = 0;
	if(format) {
		va_list va2;
		va_copy(va2, va);
		const int total_size = vsnprintf(NULL, 0, format, va2);
		va_end(va2);
		if (total_size > 0) {
			VLA(char, formatted_str, total_size + 1);
			vsprintf(formatted_str, format, va);
			ret = log_mbox(caption, type, formatted_str);
			VLA_FREE(formatted_str);
		}
	}
	return ret;
}

int log_mboxf(const char *caption, const UINT type, const char *format, ...)
{
	va_list va;
	va_start(va, format);
	int ret = log_vmboxf(caption, type, format, va);
	va_end(va);
	return ret;
}

void log_mbox_set_owner(HWND hwnd)
{
	mbox_owner_hwnd = hwnd;
}

static void OpenConsole(void)
{
	if(console_open) {
		return;
	}
	AllocConsole();

	// To match the behavior of the native Windows console, Wine additionally
	// needs read rights because its WriteConsole() implementation calls
	// GetConsoleMode(), and setvbuf() because… I don't know?
	freopen("CONOUT$", "w+b", stdout);
	setvbuf(stdout, NULL, _IONBF, 0);

	/// This breaks all normal, unlogged printf() calls to stdout!
	// _setmode(_fileno(stdout), _O_U16TEXT);

	console_open = true;
}

/// Per-module loggers
/// ------------------
std::nullptr_t logger_t::verrorf(const char *format, va_list va) const
{
	va_list va2;
	va_copy(va2, va);
	const int total_size = vsnprintf(NULL, 0, format, va2);
	va_end(va2);
	if (total_size > 0) {
		VLA(char, formatted_str, total_size + 1 + prefix.length());
		memcpy(formatted_str, prefix.data(), prefix.length());
		vsprintf(formatted_str + prefix.length(), format, va);
		log_mbox(err_caption, MB_OK | MB_ICONERROR, formatted_str);
		VLA_FREE(formatted_str);
	}
	return nullptr;
}

std::nullptr_t logger_t::errorf(const char *format, ...) const
{
	va_list va;
	va_start(va, format);
	auto ret = verrorf(format, va);
	va_end(va);
	return ret;
}
/// ------------------

void log_init(int console)
{
	CreateDirectoryU("logs", NULL);
	log_rotate();

	// Using CreateFile, _open_osfhandle and _fdopen instead of plain fopen because we need the flag FILE_SHARE_DELETE for log rotation
	log_file = CreateFileU(LOG, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
	log_queue = new ThreadPool(1);
#ifdef _DEBUG
	OpenConsole();
#else
	if(console) {
		OpenConsole();
	}
	if(log_file) {

		constexpr std::string_view DashUChar = u8"―";

		const size_t line_len = (strlen(PROJECT_NAME) + strlen(" logfile")) * DashUChar.length();
		VLA(char, line, line_len + 1);
		line[line_len] = '\0';

		for (size_t i = 0; i < line_len; i += DashUChar.length()) {
			memcpy(&line[i], DashUChar.data(), DashUChar.length());
		}

		fprintf(log_file, "%s\n", line);
		fprintf(log_file, "%s logfile\n", PROJECT_NAME);
		fprintf(log_file, "Branch: %s\n", PROJECT_BRANCH);
		fprintf(log_file, "Version: %s\n", PROJECT_VERSION_STRING);
		fprintf(log_file, "Build time: "  __DATE__ " " __TIME__ "\n");
#if defined(BUILDER_NAME_W)
		{
			const wchar_t *builder = BUILDER_NAME_W;
			UTF8_DEC(builder);
			UTF8_CONV(builder);
			fprintf(log_file, "Built by: %s\n", builder_utf8);
			UTF8_FREE(builder);
		}
#elif defined(BUILDER_NAME)
		fprintf(log_file, "Built by: %s\n", BUILDER_NAME);
#endif
		fprintf(log_file, "Command line: %s\n", GetCommandLineU());
		fprintf(log_file, "%s\n\n", line);
		fflush(log_file);
		VLA_FREE(line);
	}
#endif
	size_t cur_dir_len = GetCurrentDirectoryU(0, nullptr);
	size_t full_fn_len = cur_dir_len + sizeof(LOG);
	VLA(char, full_fn, full_fn_len);
	defer(VLA_FREE(full_fn));
	GetCurrentDirectoryU(cur_dir_len, full_fn);
	full_fn[cur_dir_len - 1] = '/';
	full_fn[cur_dir_len] = '\0';
	str_slash_normalize(full_fn); // Necessary!
	memcpy(full_fn + cur_dir_len, LOG, sizeof(LOG));

	log_filemapping = CreateFileMappingU(
		INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, 1, full_fn
	);
	if(log_file == INVALID_HANDLE_VALUE && GetLastError() != ERROR_ALREADY_EXISTS) {
		auto ret = log_mboxf(nullptr, MB_OKCANCEL | MB_ICONHAND,
			"Error creating %s: %s\n"
			"\n"
			"Logging will be unavailable. "
			"Further writes to this directory are likely to fail as well. "
			"Moving %s to a different directory will probably fix this.\n"
			"\n"
			"Continue?",
			full_fn, strerror(errno), PROJECT_NAME_SHORT
		);
		if(ret == IDCANCEL) {
			auto pExitProcess = ((void (TH_STDCALL*)(UINT))detour_top(
				"kernel32.dll", "ExitProcess", (FARPROC)thcrap_ExitProcess
			));
			pExitProcess(-1);
		}
	}
}

void log_exit(void)
{
	// Run the destructor to ensure all remaining log messages were printed
	delete log_queue;

	if(console_open)
		FreeConsole();
	if(log_file) {
		CloseHandle(log_filemapping);
		CloseHandle(log_file);
		log_file = INVALID_HANDLE_VALUE;
	}
}
<|endoftext|>


<commit_before>//===--- swift-dependency-tool.cpp - Convert binary swiftdeps to YAML -----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/AST/FileSystem.h"
#include "swift/AST/FineGrainedDependencies.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/FineGrainedDependencyFormat.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/LLVMInitialize.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"

using namespace swift;
using namespace fine_grained_dependencies;

//==============================================================================
// MARK: SourceFileDepGraph YAML reading & writing
//==============================================================================

// This introduces a redefinition where ever std::is_same_t<size_t, uint64_t>
// holds
#if !(defined(__linux__) || defined(_WIN64))
LLVM_YAML_DECLARE_SCALAR_TRAITS(size_t, QuotingType::None)
#endif
LLVM_YAML_DECLARE_ENUM_TRAITS(swift::fine_grained_dependencies::NodeKind)
LLVM_YAML_DECLARE_ENUM_TRAITS(swift::fine_grained_dependencies::DeclAspect)
LLVM_YAML_DECLARE_MAPPING_TRAITS(
    swift::fine_grained_dependencies::DependencyKey)
LLVM_YAML_DECLARE_MAPPING_TRAITS(swift::fine_grained_dependencies::DepGraphNode)

namespace llvm {
namespace yaml {
template <>
struct MappingContextTraits<
    swift::fine_grained_dependencies::SourceFileDepGraphNode,
    swift::fine_grained_dependencies::SourceFileDepGraph> {
  using SourceFileDepGraphNode =
      swift::fine_grained_dependencies::SourceFileDepGraphNode;
  using SourceFileDepGraph =
      swift::fine_grained_dependencies::SourceFileDepGraph;

  static void mapping(IO &io, SourceFileDepGraphNode &node,
                      SourceFileDepGraph &g);
};

template <>
struct SequenceTraits<
    std::vector<swift::fine_grained_dependencies::SourceFileDepGraphNode *>> {
  using SourceFileDepGraphNode =
      swift::fine_grained_dependencies::SourceFileDepGraphNode;
  using NodeVec = std::vector<SourceFileDepGraphNode *>;
  static size_t size(IO &, NodeVec &vec);
  static SourceFileDepGraphNode &element(IO &, NodeVec &vec, size_t index);
};

template <> struct ScalarTraits<swift::Fingerprint> {
  static void output(const swift::Fingerprint &fp, void *c, raw_ostream &os) {
    os << fp.getRawValue();
  }
  static StringRef input(StringRef s, void *, swift::Fingerprint &fp) {
    if (auto convertedFP = swift::Fingerprint::fromString(s))
      fp = convertedFP.getValue();
    else {
      llvm::errs() << "Failed to convert fingerprint '" << s << "'\n";
      exit(1);
    }
    return StringRef();
  }
  static QuotingType mustQuote(StringRef S) { return needsQuotes(S); }
};

} // namespace yaml
} // namespace llvm

LLVM_YAML_DECLARE_MAPPING_TRAITS(
    swift::fine_grained_dependencies::SourceFileDepGraph)

namespace llvm {
namespace yaml {
// This introduces a redefinition for Linux.
#if !(defined(__linux__) || defined(_WIN64))
void ScalarTraits<size_t>::output(const size_t &Val, void *, raw_ostream &out) {
  out << Val;
}

StringRef ScalarTraits<size_t>::input(StringRef scalar, void *ctxt,
                                      size_t &value) {
  return scalar.getAsInteger(10, value) ? "could not parse size_t" : "";
}
#endif

void ScalarEnumerationTraits<swift::fine_grained_dependencies::NodeKind>::
    enumeration(IO &io, swift::fine_grained_dependencies::NodeKind &value) {
  using NodeKind = swift::fine_grained_dependencies::NodeKind;
  io.enumCase(value, "topLevel", NodeKind::topLevel);
  io.enumCase(value, "nominal", NodeKind::nominal);
  io.enumCase(value, "potentialMember", NodeKind::potentialMember);
  io.enumCase(value, "member", NodeKind::member);
  io.enumCase(value, "dynamicLookup", NodeKind::dynamicLookup);
  io.enumCase(value, "externalDepend", NodeKind::externalDepend);
  io.enumCase(value, "sourceFileProvide", NodeKind::sourceFileProvide);
}

void ScalarEnumerationTraits<DeclAspect>::enumeration(
    IO &io, swift::fine_grained_dependencies::DeclAspect &value) {
  using DeclAspect = swift::fine_grained_dependencies::DeclAspect;
  io.enumCase(value, "interface", DeclAspect::interface);
  io.enumCase(value, "implementation", DeclAspect::implementation);
}

void MappingTraits<DependencyKey>::mapping(
    IO &io, swift::fine_grained_dependencies::DependencyKey &key) {
  io.mapRequired("kind", key.kind);
  io.mapRequired("aspect", key.aspect);
  io.mapRequired("context", key.context);
  io.mapRequired("name", key.name);
}

void MappingTraits<DepGraphNode>::mapping(
    IO &io, swift::fine_grained_dependencies::DepGraphNode &node) {
  io.mapRequired("key", node.key);
  io.mapOptional("fingerprint", node.fingerprint);
}

void MappingContextTraits<SourceFileDepGraphNode, SourceFileDepGraph>::mapping(
    IO &io, SourceFileDepGraphNode &node, SourceFileDepGraph &g) {
  MappingTraits<DepGraphNode>::mapping(io, node);
  io.mapRequired("sequenceNumber", node.sequenceNumber);
  std::vector<size_t> defsIDependUponVec(node.defsIDependUpon.begin(),
                                         node.defsIDependUpon.end());
  io.mapRequired("defsIDependUpon", defsIDependUponVec);
  io.mapRequired("isProvides", node.isProvides);
  if (!io.outputting()) {
    for (size_t u : defsIDependUponVec)
      node.defsIDependUpon.insert(u);
  }
  assert(g.getNode(node.sequenceNumber) && "Bad sequence number");
}

size_t SequenceTraits<std::vector<SourceFileDepGraphNode *>>::size(
    IO &, std::vector<SourceFileDepGraphNode *> &vec) {
  return vec.size();
}

SourceFileDepGraphNode &
SequenceTraits<std::vector<SourceFileDepGraphNode *>>::element(
    IO &, std::vector<SourceFileDepGraphNode *> &vec, size_t index) {
  while (vec.size() <= index)
    vec.push_back(new SourceFileDepGraphNode());
  return *vec[index];
}

void MappingTraits<SourceFileDepGraph>::mapping(IO &io, SourceFileDepGraph &g) {
  io.mapRequired("allNodes", g.allNodes, g);
}
} // namespace yaml
} // namespace llvm

enum class ActionType : unsigned {
  None,
  BinaryToYAML,
  YAMLToBinary
};

namespace options {

static llvm::cl::OptionCategory Category("swift-dependency-tool Options");

static llvm::cl::opt<std::string>
InputFilename("input-filename",
              llvm::cl::desc("Name of the input file"),
              llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
OutputFilename("output-filename",
               llvm::cl::desc("Name of the output file"),
               llvm::cl::cat(Category));

static llvm::cl::opt<ActionType>
Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None),
       llvm::cl::cat(Category),
       llvm::cl::values(
           clEnumValN(ActionType::BinaryToYAML,
                      "to-yaml", "Convert new binary .swiftdeps format to YAML"),
           clEnumValN(ActionType::YAMLToBinary,
                      "from-yaml", "Convert YAML to new binary .swiftdeps format")));

}

int main(int argc, char *argv[]) {
  PROGRAM_START(argc, argv);
  INITIALIZE_LLVM();

  llvm::cl::HideUnrelatedOptions(options::Category);
  llvm::cl::ParseCommandLineOptions(argc, argv, "Swift Dependency Tool\n");

  SourceManager sourceMgr;
  DiagnosticEngine diags(sourceMgr);

  switch (options::Action) {
  case ActionType::None: {
    llvm::errs() << "action required\n";
    llvm::cl::PrintHelpMessage();
    return 1;
  }

  case ActionType::BinaryToYAML: {
    auto fg = SourceFileDepGraph::loadFromPath(options::InputFilename, true);
    if (!fg) {
      llvm::errs() << "Failed to read dependency file\n";
      return 1;
    }

    bool hadError =
      withOutputFile(diags, options::OutputFilename,
        [&](llvm::raw_pwrite_stream &out) {
          out << "# Fine-grained v0\n";
          llvm::yaml::Output yamlWriter(out);
          yamlWriter << *fg;
          return false;
        });

    if (hadError) {
      llvm::errs() << "Failed to write YAML swiftdeps\n";
    }
    break;
  }

  case ActionType::YAMLToBinary: {
    auto bufferOrError = llvm::MemoryBuffer::getFile(options::InputFilename);
    if (!bufferOrError) {
      llvm::errs() << "Failed to read dependency file\n";
      return 1;
    }

    auto &buffer = *bufferOrError.get();

    SourceFileDepGraph fg;
    llvm::yaml::Input yamlReader(llvm::MemoryBufferRef(buffer), nullptr);
    yamlReader >> fg;
    if (yamlReader.error()) {
      llvm::errs() << "Failed to parse YAML swiftdeps\n";
      return 1;
    }

    if (writeFineGrainedDependencyGraphToPath(
            diags, options::OutputFilename, fg)) {
      llvm::errs() << "Failed to write binary swiftdeps\n";
      return 1;
    }

    break;
  }
  }

  return 0;
}
<commit_msg>swift-dependency-tool: avoid redefinition of ScalarTraits<unsigned> on FreeBSD<commit_after>//===--- swift-dependency-tool.cpp - Convert binary swiftdeps to YAML -----===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "swift/AST/FileSystem.h"
#include "swift/AST/FineGrainedDependencies.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/FineGrainedDependencyFormat.h"
#include "swift/Basic/SourceManager.h"
#include "swift/Basic/LLVMInitialize.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"

using namespace swift;
using namespace fine_grained_dependencies;

//==============================================================================
// MARK: SourceFileDepGraph YAML reading & writing
//==============================================================================

// This introduces a redefinition wherever std::is_same_t<size_t, uint64_t>
// holds.
#if !(defined(__linux__) || defined(_WIN64) || defined(__FreeBSD__))
LLVM_YAML_DECLARE_SCALAR_TRAITS(size_t, QuotingType::None)
#endif
LLVM_YAML_DECLARE_ENUM_TRAITS(swift::fine_grained_dependencies::NodeKind)
LLVM_YAML_DECLARE_ENUM_TRAITS(swift::fine_grained_dependencies::DeclAspect)
LLVM_YAML_DECLARE_MAPPING_TRAITS(
    swift::fine_grained_dependencies::DependencyKey)
LLVM_YAML_DECLARE_MAPPING_TRAITS(swift::fine_grained_dependencies::DepGraphNode)

namespace llvm {
namespace yaml {
template <>
struct MappingContextTraits<
    swift::fine_grained_dependencies::SourceFileDepGraphNode,
    swift::fine_grained_dependencies::SourceFileDepGraph> {
  using SourceFileDepGraphNode =
      swift::fine_grained_dependencies::SourceFileDepGraphNode;
  using SourceFileDepGraph =
      swift::fine_grained_dependencies::SourceFileDepGraph;

  static void mapping(IO &io, SourceFileDepGraphNode &node,
                      SourceFileDepGraph &g);
};

template <>
struct SequenceTraits<
    std::vector<swift::fine_grained_dependencies::SourceFileDepGraphNode *>> {
  using SourceFileDepGraphNode =
      swift::fine_grained_dependencies::SourceFileDepGraphNode;
  using NodeVec = std::vector<SourceFileDepGraphNode *>;
  static size_t size(IO &, NodeVec &vec);
  static SourceFileDepGraphNode &element(IO &, NodeVec &vec, size_t index);
};

template <> struct ScalarTraits<swift::Fingerprint> {
  static void output(const swift::Fingerprint &fp, void *c, raw_ostream &os) {
    os << fp.getRawValue();
  }
  static StringRef input(StringRef s, void *, swift::Fingerprint &fp) {
    if (auto convertedFP = swift::Fingerprint::fromString(s))
      fp = convertedFP.getValue();
    else {
      llvm::errs() << "Failed to convert fingerprint '" << s << "'\n";
      exit(1);
    }
    return StringRef();
  }
  static QuotingType mustQuote(StringRef S) { return needsQuotes(S); }
};

} // namespace yaml
} // namespace llvm

LLVM_YAML_DECLARE_MAPPING_TRAITS(
    swift::fine_grained_dependencies::SourceFileDepGraph)

namespace llvm {
namespace yaml {
// This introduces a redefinition wherever std::is_same_t<size_t, uint64_t>
// holds.
#if !(defined(__linux__) || defined(_WIN64) || defined(__FreeBSD__))
void ScalarTraits<size_t>::output(const size_t &Val, void *, raw_ostream &out) {
  out << Val;
}

StringRef ScalarTraits<size_t>::input(StringRef scalar, void *ctxt,
                                      size_t &value) {
  return scalar.getAsInteger(10, value) ? "could not parse size_t" : "";
}
#endif

void ScalarEnumerationTraits<swift::fine_grained_dependencies::NodeKind>::
    enumeration(IO &io, swift::fine_grained_dependencies::NodeKind &value) {
  using NodeKind = swift::fine_grained_dependencies::NodeKind;
  io.enumCase(value, "topLevel", NodeKind::topLevel);
  io.enumCase(value, "nominal", NodeKind::nominal);
  io.enumCase(value, "potentialMember", NodeKind::potentialMember);
  io.enumCase(value, "member", NodeKind::member);
  io.enumCase(value, "dynamicLookup", NodeKind::dynamicLookup);
  io.enumCase(value, "externalDepend", NodeKind::externalDepend);
  io.enumCase(value, "sourceFileProvide", NodeKind::sourceFileProvide);
}

void ScalarEnumerationTraits<DeclAspect>::enumeration(
    IO &io, swift::fine_grained_dependencies::DeclAspect &value) {
  using DeclAspect = swift::fine_grained_dependencies::DeclAspect;
  io.enumCase(value, "interface", DeclAspect::interface);
  io.enumCase(value, "implementation", DeclAspect::implementation);
}

void MappingTraits<DependencyKey>::mapping(
    IO &io, swift::fine_grained_dependencies::DependencyKey &key) {
  io.mapRequired("kind", key.kind);
  io.mapRequired("aspect", key.aspect);
  io.mapRequired("context", key.context);
  io.mapRequired("name", key.name);
}

void MappingTraits<DepGraphNode>::mapping(
    IO &io, swift::fine_grained_dependencies::DepGraphNode &node) {
  io.mapRequired("key", node.key);
  io.mapOptional("fingerprint", node.fingerprint);
}

void MappingContextTraits<SourceFileDepGraphNode, SourceFileDepGraph>::mapping(
    IO &io, SourceFileDepGraphNode &node, SourceFileDepGraph &g) {
  MappingTraits<DepGraphNode>::mapping(io, node);
  io.mapRequired("sequenceNumber", node.sequenceNumber);
  std::vector<size_t> defsIDependUponVec(node.defsIDependUpon.begin(),
                                         node.defsIDependUpon.end());
  io.mapRequired("defsIDependUpon", defsIDependUponVec);
  io.mapRequired("isProvides", node.isProvides);
  if (!io.outputting()) {
    for (size_t u : defsIDependUponVec)
      node.defsIDependUpon.insert(u);
  }
  assert(g.getNode(node.sequenceNumber) && "Bad sequence number");
}

size_t SequenceTraits<std::vector<SourceFileDepGraphNode *>>::size(
    IO &, std::vector<SourceFileDepGraphNode *> &vec) {
  return vec.size();
}

SourceFileDepGraphNode &
SequenceTraits<std::vector<SourceFileDepGraphNode *>>::element(
    IO &, std::vector<SourceFileDepGraphNode *> &vec, size_t index) {
  while (vec.size() <= index)
    vec.push_back(new SourceFileDepGraphNode());
  return *vec[index];
}

void MappingTraits<SourceFileDepGraph>::mapping(IO &io, SourceFileDepGraph &g) {
  io.mapRequired("allNodes", g.allNodes, g);
}
} // namespace yaml
} // namespace llvm

enum class ActionType : unsigned {
  None,
  BinaryToYAML,
  YAMLToBinary
};

namespace options {

static llvm::cl::OptionCategory Category("swift-dependency-tool Options");

static llvm::cl::opt<std::string>
InputFilename("input-filename",
              llvm::cl::desc("Name of the input file"),
              llvm::cl::cat(Category));

static llvm::cl::opt<std::string>
OutputFilename("output-filename",
               llvm::cl::desc("Name of the output file"),
               llvm::cl::cat(Category));

static llvm::cl::opt<ActionType>
Action(llvm::cl::desc("Mode:"), llvm::cl::init(ActionType::None),
       llvm::cl::cat(Category),
       llvm::cl::values(
           clEnumValN(ActionType::BinaryToYAML,
                      "to-yaml", "Convert new binary .swiftdeps format to YAML"),
           clEnumValN(ActionType::YAMLToBinary,
                      "from-yaml", "Convert YAML to new binary .swiftdeps format")));

}

int main(int argc, char *argv[]) {
  PROGRAM_START(argc, argv);
  INITIALIZE_LLVM();

  llvm::cl::HideUnrelatedOptions(options::Category);
  llvm::cl::ParseCommandLineOptions(argc, argv, "Swift Dependency Tool\n");

  SourceManager sourceMgr;
  DiagnosticEngine diags(sourceMgr);

  switch (options::Action) {
  case ActionType::None: {
    llvm::errs() << "action required\n";
    llvm::cl::PrintHelpMessage();
    return 1;
  }

  case ActionType::BinaryToYAML: {
    auto fg = SourceFileDepGraph::loadFromPath(options::InputFilename, true);
    if (!fg) {
      llvm::errs() << "Failed to read dependency file\n";
      return 1;
    }

    bool hadError =
      withOutputFile(diags, options::OutputFilename,
        [&](llvm::raw_pwrite_stream &out) {
          out << "# Fine-grained v0\n";
          llvm::yaml::Output yamlWriter(out);
          yamlWriter << *fg;
          return false;
        });

    if (hadError) {
      llvm::errs() << "Failed to write YAML swiftdeps\n";
    }
    break;
  }

  case ActionType::YAMLToBinary: {
    auto bufferOrError = llvm::MemoryBuffer::getFile(options::InputFilename);
    if (!bufferOrError) {
      llvm::errs() << "Failed to read dependency file\n";
      return 1;
    }

    auto &buffer = *bufferOrError.get();

    SourceFileDepGraph fg;
    llvm::yaml::Input yamlReader(llvm::MemoryBufferRef(buffer), nullptr);
    yamlReader >> fg;
    if (yamlReader.error()) {
      llvm::errs() << "Failed to parse YAML swiftdeps\n";
      return 1;
    }

    if (writeFineGrainedDependencyGraphToPath(
            diags, options::OutputFilename, fg)) {
      llvm::errs() << "Failed to write binary swiftdeps\n";
      return 1;
    }

    break;
  }
  }

  return 0;
}
<|endoftext|>


<commit_before>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Benoit Jacob <jacob.benoit.1@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "main.h"

template<int Alignment,typename VectorType> void map_class_vector(const VectorType& m)
{
  typedef typename VectorType::Index Index;
  typedef typename VectorType::Scalar Scalar;

  Index size = m.size();

  VectorType v = VectorType::Random(size);

  Index arraysize = 3*size;

  Scalar* a_array = internal::aligned_new<Scalar>(arraysize+1);
  Scalar* array = a_array;
  if(Alignment!=Aligned)
    array = (Scalar*)(ptrdiff_t(a_array) + (internal::packet_traits<Scalar>::AlignedOnScalar?sizeof(Scalar):sizeof(typename NumTraits<Scalar>::Real)));

  {
    Map<VectorType, Alignment, InnerStride<3> > map(array, size);
    map = v;
    for(int i = 0; i < size; ++i)
    {
      VERIFY(array[3*i] == v[i]);
      VERIFY(map[i] == v[i]);
    }
  }

  {
    Map<VectorType, Unaligned, InnerStride<Dynamic> > map(array, size, InnerStride<Dynamic>(2));
    map = v;
    for(int i = 0; i < size; ++i)
    {
      VERIFY(array[2*i] == v[i]);
      VERIFY(map[i] == v[i]);
    }
  }

  internal::aligned_delete(a_array, arraysize+1);
}

template<int Alignment,typename MatrixType> void map_class_matrix(const MatrixType& _m)
{
  typedef typename MatrixType::Index Index;
  typedef typename MatrixType::Scalar Scalar;

  Index rows = _m.rows(), cols = _m.cols();

  MatrixType m = MatrixType::Random(rows,cols);

  Index arraysize = 2*(rows+4)*(cols+4);

  Scalar* a_array = internal::aligned_new<Scalar>(arraysize+1);
  Scalar* array = a_array;
  if(Alignment!=Aligned)
    array = (Scalar*)(ptrdiff_t(a_array) + (internal::packet_traits<Scalar>::AlignedOnScalar?sizeof(Scalar):sizeof(typename NumTraits<Scalar>::Real)));

  // test no inner stride and some dynamic outer stride
  {
    Map<MatrixType, Alignment, OuterStride<Dynamic> > map(array, rows, cols, OuterStride<Dynamic>(m.innerSize()+1));
    map = m;
    VERIFY(map.outerStride() == map.innerSize()+1);
    for(int i = 0; i < m.outerSize(); ++i)
      for(int j = 0; j < m.innerSize(); ++j)
      {
        VERIFY(array[map.outerStride()*i+j] == m.coeffByOuterInner(i,j));
        VERIFY(map.coeffByOuterInner(i,j) == m.coeffByOuterInner(i,j));
      }
  }

  // test no inner stride and an outer stride of +4. This is quite important as for fixed-size matrices,
  // this allows to hit the special case where it's vectorizable.
  {
    enum {
      InnerSize = MatrixType::InnerSizeAtCompileTime,
      OuterStrideAtCompileTime = InnerSize==Dynamic ? Dynamic : InnerSize+4
    };
    Map<MatrixType, Alignment, OuterStride<OuterStrideAtCompileTime> >
      map(array, rows, cols, OuterStride<OuterStrideAtCompileTime>(m.innerSize()+4));
    map = m;
    VERIFY(map.outerStride() == map.innerSize()+4);
    for(int i = 0; i < m.outerSize(); ++i)
      for(int j = 0; j < m.innerSize(); ++j)
      {
        VERIFY(array[map.outerStride()*i+j] == m.coeffByOuterInner(i,j));
        VERIFY(map.coeffByOuterInner(i,j) == m.coeffByOuterInner(i,j));
      }
  }

  // test both inner stride and outer stride
  {
    Map<MatrixType, Alignment, Stride<Dynamic,Dynamic> > map(array, rows, cols, Stride<Dynamic,Dynamic>(2*m.innerSize()+1, 2));
    map = m;
    VERIFY(map.outerStride() == 2*map.innerSize()+1);
    VERIFY(map.innerStride() == 2);
    for(int i = 0; i < m.outerSize(); ++i)
      for(int j = 0; j < m.innerSize(); ++j)
      {
        VERIFY(array[map.outerStride()*i+map.innerStride()*j] == m.coeffByOuterInner(i,j));
        VERIFY(map.coeffByOuterInner(i,j) == m.coeffByOuterInner(i,j));
      }
  }

  internal::aligned_delete(a_array, arraysize+1);
}

void test_mapstride()
{
  for(int i = 0; i < g_repeat; i++) {
    int maxn = 30;
    CALL_SUBTEST_1( map_class_vector<Aligned>(Matrix<float, 1, 1>()) );
    CALL_SUBTEST_1( map_class_vector<Unaligned>(Matrix<float, 1, 1>()) );
    CALL_SUBTEST_2( map_class_vector<Aligned>(Vector4d()) );
    CALL_SUBTEST_2( map_class_vector<Unaligned>(Vector4d()) );
    CALL_SUBTEST_3( map_class_vector<Aligned>(RowVector4f()) );
    CALL_SUBTEST_3( map_class_vector<Unaligned>(RowVector4f()) );
    CALL_SUBTEST_4( map_class_vector<Aligned>(VectorXcf(internal::random<int>(1,maxn))) );
    CALL_SUBTEST_4( map_class_vector<Unaligned>(VectorXcf(internal::random<int>(1,maxn))) );
    CALL_SUBTEST_5( map_class_vector<Aligned>(VectorXi(internal::random<int>(1,maxn))) );
    CALL_SUBTEST_5( map_class_vector<Unaligned>(VectorXi(internal::random<int>(1,maxn))) );

    CALL_SUBTEST_1( map_class_matrix<Aligned>(Matrix<float, 1, 1>()) );
    CALL_SUBTEST_1( map_class_matrix<Unaligned>(Matrix<float, 1, 1>()) );
    CALL_SUBTEST_2( map_class_matrix<Aligned>(Matrix4d()) );
    CALL_SUBTEST_2( map_class_matrix<Unaligned>(Matrix4d()) );
    CALL_SUBTEST_3( map_class_matrix<Aligned>(Matrix<float,3,5>()) );
    CALL_SUBTEST_3( map_class_matrix<Unaligned>(Matrix<float,3,5>()) );
    CALL_SUBTEST_3( map_class_matrix<Aligned>(Matrix<float,4,8>()) );
    CALL_SUBTEST_3( map_class_matrix<Unaligned>(Matrix<float,4,8>()) );
    CALL_SUBTEST_4( map_class_matrix<Aligned>(MatrixXcf(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );
    CALL_SUBTEST_4( map_class_matrix<Unaligned>(MatrixXcf(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );
    CALL_SUBTEST_5( map_class_matrix<Aligned>(MatrixXi(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );
    CALL_SUBTEST_5( map_class_matrix<Unaligned>(MatrixXi(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );
    CALL_SUBTEST_6( map_class_matrix<Aligned>(MatrixXcd(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );
    CALL_SUBTEST_6( map_class_matrix<Unaligned>(MatrixXcd(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );

    TEST_SET_BUT_UNUSED_VARIABLE(maxn);
  }
}
<commit_msg>Extend unit test of Map<,,Stride<>> with stack allocated buffers and less trivial operations.<commit_after>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Benoit Jacob <jacob.benoit.1@gmail.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include "main.h"

template<int Alignment,typename VectorType> void map_class_vector(const VectorType& m)
{
  typedef typename VectorType::Index Index;
  typedef typename VectorType::Scalar Scalar;

  Index size = m.size();

  VectorType v = VectorType::Random(size);

  Index arraysize = 3*size;

  Scalar* a_array = internal::aligned_new<Scalar>(arraysize+1);
  Scalar* array = a_array;
  if(Alignment!=Aligned)
    array = (Scalar*)(ptrdiff_t(a_array) + (internal::packet_traits<Scalar>::AlignedOnScalar?sizeof(Scalar):sizeof(typename NumTraits<Scalar>::Real)));

  {
    Map<VectorType, Alignment, InnerStride<3> > map(array, size);
    map = v;
    for(int i = 0; i < size; ++i)
    {
      VERIFY(array[3*i] == v[i]);
      VERIFY(map[i] == v[i]);
    }
  }

  {
    Map<VectorType, Unaligned, InnerStride<Dynamic> > map(array, size, InnerStride<Dynamic>(2));
    map = v;
    for(int i = 0; i < size; ++i)
    {
      VERIFY(array[2*i] == v[i]);
      VERIFY(map[i] == v[i]);
    }
  }

  internal::aligned_delete(a_array, arraysize+1);
}

template<int Alignment,typename MatrixType> void map_class_matrix(const MatrixType& _m)
{
  typedef typename MatrixType::Index Index;
  typedef typename MatrixType::Scalar Scalar;

  Index rows = _m.rows(), cols = _m.cols();

  MatrixType m = MatrixType::Random(rows,cols);
  Scalar s1 = internal::random<Scalar>();

  Index arraysize = 2*(rows+4)*(cols+4);

  Scalar* a_array1 = internal::aligned_new<Scalar>(arraysize+1);
  Scalar* array1 = a_array1;
  if(Alignment!=Aligned)
    array1 = (Scalar*)(std::ptrdiff_t(a_array1) + (internal::packet_traits<Scalar>::AlignedOnScalar?sizeof(Scalar):sizeof(typename NumTraits<Scalar>::Real)));

  Scalar a_array2[256];
  Scalar* array2 = a_array2;
  if(Alignment!=Aligned)
    array2 = (Scalar*)(std::ptrdiff_t(a_array2) + (internal::packet_traits<Scalar>::AlignedOnScalar?sizeof(Scalar):sizeof(typename NumTraits<Scalar>::Real)));
  else
    array2 = (Scalar*)(((std::size_t(a_array2)+EIGEN_ALIGN_BYTES-1)/EIGEN_ALIGN_BYTES)*EIGEN_ALIGN_BYTES);
  Index maxsize2 = a_array2 - array2 + 256;

  // test no inner stride and some dynamic outer stride
  for(int k=0; k<2; ++k)
  {
    if(k==1 && (m.innerSize()+1)*m.outerSize() > maxsize2)
      break;
    Scalar* array = (k==0 ? array1 : array2);

    Map<MatrixType, Alignment, OuterStride<Dynamic> > map(array, rows, cols, OuterStride<Dynamic>(m.innerSize()+1));
    map = m;
    VERIFY(map.outerStride() == map.innerSize()+1);
    for(int i = 0; i < m.outerSize(); ++i)
      for(int j = 0; j < m.innerSize(); ++j)
      {
        VERIFY(array[map.outerStride()*i+j] == m.coeffByOuterInner(i,j));
        VERIFY(map.coeffByOuterInner(i,j) == m.coeffByOuterInner(i,j));
      }
    VERIFY_IS_APPROX(s1*map,s1*m);
    map *= s1;
    VERIFY_IS_APPROX(map,s1*m);
  }

  // test no inner stride and an outer stride of +4. This is quite important as for fixed-size matrices,
  // this allows to hit the special case where it's vectorizable.
  for(int k=0; k<2; ++k)
  {
    if(k==1 && (m.innerSize()+4)*m.outerSize() > maxsize2)
      break;
    Scalar* array = (k==0 ? array1 : array2);

    enum {
      InnerSize = MatrixType::InnerSizeAtCompileTime,
      OuterStrideAtCompileTime = InnerSize==Dynamic ? Dynamic : InnerSize+4
    };
    Map<MatrixType, Alignment, OuterStride<OuterStrideAtCompileTime> >
      map(array, rows, cols, OuterStride<OuterStrideAtCompileTime>(m.innerSize()+4));
    map = m;
    VERIFY(map.outerStride() == map.innerSize()+4);
    for(int i = 0; i < m.outerSize(); ++i)
      for(int j = 0; j < m.innerSize(); ++j)
      {
        VERIFY(array[map.outerStride()*i+j] == m.coeffByOuterInner(i,j));
        VERIFY(map.coeffByOuterInner(i,j) == m.coeffByOuterInner(i,j));
      }
    VERIFY_IS_APPROX(s1*map,s1*m);
    map *= s1;
    VERIFY_IS_APPROX(map,s1*m);
  }

  // test both inner stride and outer stride
  for(int k=0; k<2; ++k)
  {
    if(k==1 && (2*m.innerSize()+1)*(m.outerSize()*2) > maxsize2)
      break;
    Scalar* array = (k==0 ? array1 : array2);

    Map<MatrixType, Alignment, Stride<Dynamic,Dynamic> > map(array, rows, cols, Stride<Dynamic,Dynamic>(2*m.innerSize()+1, 2));
    map = m;
    VERIFY(map.outerStride() == 2*map.innerSize()+1);
    VERIFY(map.innerStride() == 2);
    for(int i = 0; i < m.outerSize(); ++i)
      for(int j = 0; j < m.innerSize(); ++j)
      {
        VERIFY(array[map.outerStride()*i+map.innerStride()*j] == m.coeffByOuterInner(i,j));
        VERIFY(map.coeffByOuterInner(i,j) == m.coeffByOuterInner(i,j));
      }
    VERIFY_IS_APPROX(s1*map,s1*m);
    map *= s1;
    VERIFY_IS_APPROX(map,s1*m);
  }

  internal::aligned_delete(a_array1, arraysize+1);
}

void test_mapstride()
{
  for(int i = 0; i < g_repeat; i++) {
    int maxn = 30;
    CALL_SUBTEST_1( map_class_vector<Aligned>(Matrix<float, 1, 1>()) );
    CALL_SUBTEST_1( map_class_vector<Unaligned>(Matrix<float, 1, 1>()) );
    CALL_SUBTEST_2( map_class_vector<Aligned>(Vector4d()) );
    CALL_SUBTEST_2( map_class_vector<Unaligned>(Vector4d()) );
    CALL_SUBTEST_3( map_class_vector<Aligned>(RowVector4f()) );
    CALL_SUBTEST_3( map_class_vector<Unaligned>(RowVector4f()) );
    CALL_SUBTEST_4( map_class_vector<Aligned>(VectorXcf(internal::random<int>(1,maxn))) );
    CALL_SUBTEST_4( map_class_vector<Unaligned>(VectorXcf(internal::random<int>(1,maxn))) );
    CALL_SUBTEST_5( map_class_vector<Aligned>(VectorXi(internal::random<int>(1,maxn))) );
    CALL_SUBTEST_5( map_class_vector<Unaligned>(VectorXi(internal::random<int>(1,maxn))) );

    CALL_SUBTEST_1( map_class_matrix<Aligned>(Matrix<float, 1, 1>()) );
    CALL_SUBTEST_1( map_class_matrix<Unaligned>(Matrix<float, 1, 1>()) );
    CALL_SUBTEST_2( map_class_matrix<Aligned>(Matrix4d()) );
    CALL_SUBTEST_2( map_class_matrix<Unaligned>(Matrix4d()) );
    CALL_SUBTEST_3( map_class_matrix<Aligned>(Matrix<float,3,5>()) );
    CALL_SUBTEST_3( map_class_matrix<Unaligned>(Matrix<float,3,5>()) );
    CALL_SUBTEST_3( map_class_matrix<Aligned>(Matrix<float,4,8>()) );
    CALL_SUBTEST_3( map_class_matrix<Unaligned>(Matrix<float,4,8>()) );
    CALL_SUBTEST_4( map_class_matrix<Aligned>(MatrixXcf(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );
    CALL_SUBTEST_4( map_class_matrix<Unaligned>(MatrixXcf(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );
    CALL_SUBTEST_5( map_class_matrix<Aligned>(MatrixXi(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );
    CALL_SUBTEST_5( map_class_matrix<Unaligned>(MatrixXi(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );
    CALL_SUBTEST_6( map_class_matrix<Aligned>(MatrixXcd(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );
    CALL_SUBTEST_6( map_class_matrix<Unaligned>(MatrixXcd(internal::random<int>(1,maxn),internal::random<int>(1,maxn))) );

    TEST_SET_BUT_UNUSED_VARIABLE(maxn);
  }
}
<|endoftext|>


<commit_before>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/pldm/common/pldm_request.C $                          */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2022                             */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

/* @brief Provides the IPL and runtime implementations of the
 *        sendrecv_pldm_request_impl function.
 */

#include <pldm/pldm_request.H>

using namespace PLDM;

#ifdef __HOSTBOOT_RUNTIME

/* @brief  Check the next incoming MCTP packet and see whether it's a response
 *         to the request we sent. If there is no response available at the time
 *         and i_last_chance is true, create an error log for the timeout.
 *         If i_last_chance is false, sleep for 10 milliseconds instead.
 *
 * @param[in]     i_pldm_request        The PLDM request that we are waiting on a response for
 * @param[in]     i_last_chance         The number of milliseconds remaining before timing out
 * @return        errlHndl_t            Error on timeout, nullptr otherwise
 */
errlHndl_t check_pldm_response(const pldm_msg* const i_pldm_request,
                               const bool i_last_chance)
{
    errlHndl_t errl = nullptr;

    const int request_instance_id = i_pldm_request->hdr.instance_id;

    const int return_code = MCTP::get_next_packet();

    if (return_code)
    {
        if (return_code == HBRT_RC_NO_MCTP_PACKET && !i_last_chance)
        {
            const uint64_t sec = 0;
            const uint64_t nsec = 10 * NS_PER_MSEC; // 10 milliseconds is the
                                                    // minimum supported wait
                                                    // time for hypervisor
                                                    // adjuncts
            nanosleep(sec, nsec);
        }
        else
        {
            if (return_code == HBRT_RC_NO_MCTP_PACKET)
            {
                PLDM_ERR("Timed out waiting for next PLDM message");
            }
            else
            {
                PLDM_ERR("Unexpected Return Code: 0x%.16llX", return_code);
            }

            /*@
             * @moduleid   MOD_MAKE_PLDM_REQUEST_RUNTIME
             * @reasoncode RC_RECV_FAIL
             * @userdata1  Return code returned by MCTP::get_next_packet
             * @userdata2  Header of pldm request
             * @devdesc    Software problem, failed to get next PLDM message
             * @custdesc   A software error occurred during host/bmc
             *             communication
             */
            errl = new ErrlEntry(ERRL_SEV_UNRECOVERABLE,
                                 MOD_MAKE_PLDM_REQUEST_RUNTIME,
                                 RC_RECV_FAIL,
                                 return_code,
                                 pldmHdrToUint64(*i_pldm_request),
                                 ErrlEntry::NO_SW_CALLOUT);

            addBmcAndHypErrorCallouts(errl);
        }
    }
    else
    {
        const auto& response = PLDM::get_next_response();

        if (!response.empty())
        {
            const auto response_msg = reinterpret_cast<const pldm_msg*>(response.data());

            // If we get a response to some other request, ignore the response and
            // keep looking for the instance ID we were told to expect.
            if (request_instance_id != response_msg->hdr.instance_id)
            {
                PLDM_INF("Received a response with an unexpected instance ID (got %d, expected %d); dropping message",
                         response_msg->hdr.instance_id, request_instance_id);

                PLDM::clear_next_response();
            }
        }
    }

    return errl;
}

timespec_t operator-(timespec_t lhs, timespec_t rhs)
{
    return { lhs.tv_sec - rhs.tv_sec, lhs.tv_nsec - rhs.tv_nsec };
}

errlHndl_t PLDM::sendrecv_pldm_request_impl(const std::vector<uint8_t>& i_msg,
                                            std::vector<uint8_t>& o_response_bytes)
{
    errlHndl_t errl = nullptr;

    // The PLDM instance ID of the next outgoing request
    static int instance_id = 0;

    do
    {

        const auto msgbuf = reinterpret_cast<const mctp_pldm_msg<0>*>(i_msg.data());
        const pldm_msg* const pldm_request = &msgbuf->pldm_message;

        const_cast<pldm_msg*>(pldm_request)->hdr.instance_id = instance_id;

        // This function needs to be reentrant; do this now in case the below
        // code causes a PLDM request to be sent.
        // instance_id is a 5-bit field in the PLDM header.
        instance_id = (instance_id + 1) & 0x1F;

        PLDM::clear_next_response(); // This should already be empty, but we
                                     // clear it here just to make sure.

        // Try to send the PLDM message at least once, at most twice. The
        // difference between a retry and a real message send is that a retry
        // uses the same sequence ID as any retried messages.
        const int retries = 3;

        for (int attempt = 0; PLDM::get_next_response().empty() && attempt < retries; ++attempt)
        {
            if (errl)
            { // ignore errors from previous tries if we're going to try again
                PLDM_INF("Timed out sending PLDM request with instance ID = %d; retrying",
                         pldm_request->hdr.instance_id);
                delete errl;
                errl = nullptr;
            }

            const int return_code = MCTP::send_message(i_msg);

            if (return_code)
            {
                /*@
                 * @moduleid   MOD_MAKE_PLDM_REQUEST_RUNTIME
                 * @reasoncode RC_SEND_FAIL
                 * @userdata1  Return code returned by MCTP::send_message
                 * @userdata2  Header of pldm request
                 * @devdesc    Software problem, failed to decode PLDM message
                 * @custdesc   A software error occurred during host/bmc
                 *             communication
                 */
                errl = new ErrlEntry(ERRL_SEV_UNRECOVERABLE,
                                     MOD_MAKE_PLDM_REQUEST_RUNTIME,
                                     RC_SEND_FAIL,
                                     return_code,
                                     pldmHdrToUint64(*pldm_request),
                                     ErrlEntry::NO_SW_CALLOUT);
                addBmcAndHypErrorCallouts(errl);

                PLDM_ERR("sendrecv_pldm_request_impl: Unable to send MCTP message (rc=%d, PLID=0x%08x)",
                         return_code, ERRL_GETPLID_SAFE(errl));

                break;
            }

            const uint64_t timeout_seconds = 90;
            timespec_t starttime = { }, now = { };

            assert(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);

            PLDM_INF("Started polling at %d", starttime.tv_sec);

            uint64_t pollcount = 0;

            do
            {
                assert(clock_gettime(CLOCK_MONOTONIC, &now) == 0);

                // Note that if this is a retry, we are sending the request
                // using the same instance ID. This allows the BMC to handle
                // duplicate requests appropriately if for some reason they
                // received our first request and acted on it, but we did not
                // receive their first response.
                errl = check_pldm_response(pldm_request,
                                           (now - starttime).tv_sec > timeout_seconds);

                ++pollcount;
            } while (!errl && PLDM::get_next_response().empty());

            PLDM_INF("Stopped polling at %d, took %d seconds, polled %d times", now.tv_sec,
                     (now - starttime).tv_sec,
                     pollcount);
        }

        // break if we encounter an error
        if (errl)
        {
            break;
        }

        o_response_bytes = move(PLDM::get_next_response());

        PLDM::clear_next_response();

        const size_t MIN_RESP_LEN = sizeof(pldm_msg_hdr) + 1;
        if (o_response_bytes.size() < MIN_RESP_LEN)
        {
            auto hdr64 = pldmHdrToUint64(*reinterpret_cast<pldm_msg*>(o_response_bytes.data()));
            PLDM_ERR("The length of the response to request (0x%lx) was %d bytes when at least %d bytes was expected",
                     hdr64, o_response_bytes.size(), MIN_RESP_LEN);
            /*@
             * @moduleid   MOD_MAKE_PLDM_REQUEST_RUNTIME
             * @reasoncode RC_INVALID_LENGTH
             * @userdata1  Actual Response Length
             * @userdata2  Header of original pldm request
             * @devdesc    PLDM Response from BMC is too small.
             * @custdesc   A host failure occurred
             */
            errl = new ErrlEntry(ERRL_SEV_UNRECOVERABLE,
                                 MOD_MAKE_PLDM_REQUEST_RUNTIME,
                                 RC_INVALID_LENGTH,
                                 o_response_bytes.size(),
                                 hdr64);
            addBmcErrorCallouts(errl);
            break;
        }
    } while (false);

    return errl;
}

#else

errlHndl_t PLDM::sendrecv_pldm_request_impl(const msg_q_t i_msgQ,
                                            const std::vector<uint8_t>& i_msg,
                                            std::unique_ptr<uint8_t, decltype(&free)>& o_response,
                                            size_t& o_response_length)
{
    errlHndl_t errl = nullptr;

    do
    {
        const std::unique_ptr<msg_t, decltype(&msg_free)> msg { msg_allocate(), msg_free };
        msg->data[0] = i_msg.size();
        msg->extra_data = const_cast<uint8_t*>(i_msg.data());

        // Place message on the queue and wait for a response
        const int return_code = msg_sendrecv(i_msgQ, msg.get());

        const auto msgbuf = reinterpret_cast<const mctp_pldm_msg<0>*>(i_msg.data());
        const pldm_msg* const pldm_request = &(msgbuf->pldm_message);

        if (return_code)
        {
            const uint64_t request_hdr_data = pldmHdrToUint64(*pldm_request);
            /*@
             * @moduleid   MOD_MAKE_PLDM_REQUEST
             * @reasoncode RC_SENDRECV_FAIL
             * @userdata1  Return code returned by msg_sendrecv
             * @userdata2  Header of pldm request
             * @devdesc    Software problem, failed to decode PLDM message
             * @custdesc   A software error occurred during system boot
             */
            errl = new ErrlEntry(ERRL_SEV_UNRECOVERABLE,
                                 MOD_MAKE_PLDM_REQUEST,
                                 RC_SENDRECV_FAIL,
                                 return_code,
                                 request_hdr_data,
                                 ErrlEntry::ADD_SW_CALLOUT);

            PLDM_ERR("sendrecv_pldm_request_impl: msg_sendrecv failed (rc=%d, PLID=0x%08x)",
                     return_code, ERRL_GETPLID_SAFE(errl));

            errl->collectTrace(PLDM_COMP_NAME);
            break;
        }
        else if (msg->data[1] != REQ_SUCCESS)
        {
            errl = reinterpret_cast<errlHndl_t>(msg->extra_data);
            msg->extra_data = nullptr;
            break;
        }

        // we own the memory pointed to by msg->extra_data
        o_response.reset(static_cast<uint8_t*>(msg->extra_data));
        o_response_length = msg->data[0];

        const size_t MIN_RESP_LEN = sizeof(pldm_msg_hdr) + 1;
        if (o_response_length < MIN_RESP_LEN)
        {
            auto hdr64 = pldmHdrToUint64(*reinterpret_cast<pldm_msg*>(o_response.get()));
            PLDM_ERR("The length of the response to request (0x%lx) was %d bytes when at least %d bytes was expected",
                     hdr64, o_response_length, MIN_RESP_LEN);

            /*@
             * @moduleid   MOD_MAKE_PLDM_REQUEST
             * @reasoncode RC_INVALID_LENGTH
             * @userdata1  Length of response in bytes
             * @userdata2  First bytes of PLDM header
             * @devdesc    Software problem, PLDM response message was too short
             * @custdesc   A software error occurred during system boot
             */
            errl = new ErrlEntry(ERRL_SEV_UNRECOVERABLE,
                                 MOD_MAKE_PLDM_REQUEST,
                                 RC_INVALID_LENGTH,
                                 o_response_length,
                                 hdr64);
            addBmcErrorCallouts(errl);
            break;
        }
    } while (false);

    return errl;
}

#endif
<commit_msg>Reduce PLDM timeout, increase retry numbers at runtime<commit_after>/* IBM_PROLOG_BEGIN_TAG                                                   */
/* This is an automatically generated prolog.                             */
/*                                                                        */
/* $Source: src/usr/pldm/common/pldm_request.C $                          */
/*                                                                        */
/* OpenPOWER HostBoot Project                                             */
/*                                                                        */
/* Contributors Listed Below - COPYRIGHT 2022                             */
/* [+] International Business Machines Corp.                              */
/*                                                                        */
/*                                                                        */
/* Licensed under the Apache License, Version 2.0 (the "License");        */
/* you may not use this file except in compliance with the License.       */
/* You may obtain a copy of the License at                                */
/*                                                                        */
/*     http://www.apache.org/licenses/LICENSE-2.0                         */
/*                                                                        */
/* Unless required by applicable law or agreed to in writing, software    */
/* distributed under the License is distributed on an "AS IS" BASIS,      */
/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or        */
/* implied. See the License for the specific language governing           */
/* permissions and limitations under the License.                         */
/*                                                                        */
/* IBM_PROLOG_END_TAG                                                     */

/* @brief Provides the IPL and runtime implementations of the
 *        sendrecv_pldm_request_impl function.
 */

#include <pldm/pldm_request.H>

using namespace PLDM;

#ifdef __HOSTBOOT_RUNTIME

/* @brief  Check the next incoming MCTP packet and see whether it's a response
 *         to the request we sent. If there is no response available at the time
 *         and i_last_chance is true, create an error log for the timeout.
 *         If i_last_chance is false, sleep for 10 milliseconds instead.
 *
 * @param[in]     i_pldm_request        The PLDM request that we are waiting on a response for
 * @param[in]     i_last_chance         The number of milliseconds remaining before timing out
 * @return        errlHndl_t            Error on timeout, nullptr otherwise
 */
errlHndl_t check_pldm_response(const pldm_msg* const i_pldm_request,
                               const bool i_last_chance)
{
    errlHndl_t errl = nullptr;

    const int request_instance_id = i_pldm_request->hdr.instance_id;

    const int return_code = MCTP::get_next_packet();

    if (return_code)
    {
        if (return_code == HBRT_RC_NO_MCTP_PACKET && !i_last_chance)
        {
            const uint64_t sec = 0;
            const uint64_t nsec = 10 * NS_PER_MSEC; // 10 milliseconds is the
                                                    // minimum supported wait
                                                    // time for hypervisor
                                                    // adjuncts
            nanosleep(sec, nsec);
        }
        else
        {
            if (return_code == HBRT_RC_NO_MCTP_PACKET)
            {
                PLDM_ERR("Timed out waiting for next PLDM message");
            }
            else
            {
                PLDM_ERR("Unexpected Return Code: 0x%.16llX", return_code);
            }

            /*@
             * @moduleid   MOD_MAKE_PLDM_REQUEST_RUNTIME
             * @reasoncode RC_RECV_FAIL
             * @userdata1  Return code returned by MCTP::get_next_packet
             * @userdata2  Header of pldm request
             * @devdesc    Software problem, failed to get next PLDM message
             * @custdesc   A software error occurred during host/bmc
             *             communication
             */
            errl = new ErrlEntry(ERRL_SEV_UNRECOVERABLE,
                                 MOD_MAKE_PLDM_REQUEST_RUNTIME,
                                 RC_RECV_FAIL,
                                 return_code,
                                 pldmHdrToUint64(*i_pldm_request),
                                 ErrlEntry::NO_SW_CALLOUT);

            addBmcAndHypErrorCallouts(errl);
        }
    }
    else
    {
        const auto& response = PLDM::get_next_response();

        if (!response.empty())
        {
            const auto response_msg = reinterpret_cast<const pldm_msg*>(response.data());

            // If we get a response to some other request, ignore the response and
            // keep looking for the instance ID we were told to expect.
            if (request_instance_id != response_msg->hdr.instance_id)
            {
                PLDM_INF("Received a response with an unexpected instance ID (got %d, expected %d); dropping message",
                         response_msg->hdr.instance_id, request_instance_id);

                PLDM::clear_next_response();
            }
        }
    }

    return errl;
}

timespec_t operator-(timespec_t lhs, timespec_t rhs)
{
    return { lhs.tv_sec - rhs.tv_sec, lhs.tv_nsec - rhs.tv_nsec };
}

errlHndl_t PLDM::sendrecv_pldm_request_impl(const std::vector<uint8_t>& i_msg,
                                            std::vector<uint8_t>& o_response_bytes)
{
    errlHndl_t errl = nullptr;

    // The PLDM instance ID of the next outgoing request
    static int instance_id = 0;

    do
    {

        const auto msgbuf = reinterpret_cast<const mctp_pldm_msg<0>*>(i_msg.data());
        const pldm_msg* const pldm_request = &msgbuf->pldm_message;

        const_cast<pldm_msg*>(pldm_request)->hdr.instance_id = instance_id;

        // This function needs to be reentrant; do this now in case the below
        // code causes a PLDM request to be sent.
        // instance_id is a 5-bit field in the PLDM header.
        instance_id = (instance_id + 1) & 0x1F;

        PLDM::clear_next_response(); // This should already be empty, but we
                                     // clear it here just to make sure.

        // Try to send the PLDM message, retry if it times out. The
        // difference between a retry and a real message send is that a retry
        // uses the same sequence ID as any retried messages.
        const int retries = 9;

        for (int attempt = 0; PLDM::get_next_response().empty() && attempt < retries; ++attempt)
        {
            if (errl)
            { // ignore errors from previous tries if we're going to try again
                PLDM_INF("Timed out sending PLDM request with instance ID = %d; retrying",
                         pldm_request->hdr.instance_id);
                delete errl;
                errl = nullptr;
            }

            const int return_code = MCTP::send_message(i_msg);

            if (return_code)
            {
                /*@
                 * @moduleid   MOD_MAKE_PLDM_REQUEST_RUNTIME
                 * @reasoncode RC_SEND_FAIL
                 * @userdata1  Return code returned by MCTP::send_message
                 * @userdata2  Header of pldm request
                 * @devdesc    Software problem, failed to decode PLDM message
                 * @custdesc   A software error occurred during host/bmc
                 *             communication
                 */
                errl = new ErrlEntry(ERRL_SEV_UNRECOVERABLE,
                                     MOD_MAKE_PLDM_REQUEST_RUNTIME,
                                     RC_SEND_FAIL,
                                     return_code,
                                     pldmHdrToUint64(*pldm_request),
                                     ErrlEntry::NO_SW_CALLOUT);
                addBmcAndHypErrorCallouts(errl);

                PLDM_ERR("sendrecv_pldm_request_impl: Unable to send MCTP message (rc=%d, PLID=0x%08x)",
                         return_code, ERRL_GETPLID_SAFE(errl));

                break;
            }

            const uint64_t timeout_seconds = 30;
            timespec_t starttime = { }, now = { };

            assert(clock_gettime(CLOCK_MONOTONIC, &starttime) == 0);

            PLDM_INF("Started polling at %d", starttime.tv_sec);

            uint64_t pollcount = 0;

            do
            {
                assert(clock_gettime(CLOCK_MONOTONIC, &now) == 0);

                // Note that if this is a retry, we are sending the request
                // using the same instance ID. This allows the BMC to handle
                // duplicate requests appropriately if for some reason they
                // received our first request and acted on it, but we did not
                // receive their first response.
                errl = check_pldm_response(pldm_request,
                                           (now - starttime).tv_sec > timeout_seconds);

                ++pollcount;
            } while (!errl && PLDM::get_next_response().empty());

            PLDM_INF("Stopped polling at %d, took %d seconds, polled %d times", now.tv_sec,
                     (now - starttime).tv_sec,
                     pollcount);
        }

        // break if we encounter an error
        if (errl)
        {
            break;
        }

        o_response_bytes = move(PLDM::get_next_response());

        PLDM::clear_next_response();

        const size_t MIN_RESP_LEN = sizeof(pldm_msg_hdr) + 1;
        if (o_response_bytes.size() < MIN_RESP_LEN)
        {
            auto hdr64 = pldmHdrToUint64(*reinterpret_cast<pldm_msg*>(o_response_bytes.data()));
            PLDM_ERR("The length of the response to request (0x%lx) was %d bytes when at least %d bytes was expected",
                     hdr64, o_response_bytes.size(), MIN_RESP_LEN);
            /*@
             * @moduleid   MOD_MAKE_PLDM_REQUEST_RUNTIME
             * @reasoncode RC_INVALID_LENGTH
             * @userdata1  Actual Response Length
             * @userdata2  Header of original pldm request
             * @devdesc    PLDM Response from BMC is too small.
             * @custdesc   A host failure occurred
             */
            errl = new ErrlEntry(ERRL_SEV_UNRECOVERABLE,
                                 MOD_MAKE_PLDM_REQUEST_RUNTIME,
                                 RC_INVALID_LENGTH,
                                 o_response_bytes.size(),
                                 hdr64);
            addBmcErrorCallouts(errl);
            break;
        }
    } while (false);

    return errl;
}

#else

errlHndl_t PLDM::sendrecv_pldm_request_impl(const msg_q_t i_msgQ,
                                            const std::vector<uint8_t>& i_msg,
                                            std::unique_ptr<uint8_t, decltype(&free)>& o_response,
                                            size_t& o_response_length)
{
    errlHndl_t errl = nullptr;

    do
    {
        const std::unique_ptr<msg_t, decltype(&msg_free)> msg { msg_allocate(), msg_free };
        msg->data[0] = i_msg.size();
        msg->extra_data = const_cast<uint8_t*>(i_msg.data());

        // Place message on the queue and wait for a response
        const int return_code = msg_sendrecv(i_msgQ, msg.get());

        const auto msgbuf = reinterpret_cast<const mctp_pldm_msg<0>*>(i_msg.data());
        const pldm_msg* const pldm_request = &(msgbuf->pldm_message);

        if (return_code)
        {
            const uint64_t request_hdr_data = pldmHdrToUint64(*pldm_request);
            /*@
             * @moduleid   MOD_MAKE_PLDM_REQUEST
             * @reasoncode RC_SENDRECV_FAIL
             * @userdata1  Return code returned by msg_sendrecv
             * @userdata2  Header of pldm request
             * @devdesc    Software problem, failed to decode PLDM message
             * @custdesc   A software error occurred during system boot
             */
            errl = new ErrlEntry(ERRL_SEV_UNRECOVERABLE,
                                 MOD_MAKE_PLDM_REQUEST,
                                 RC_SENDRECV_FAIL,
                                 return_code,
                                 request_hdr_data,
                                 ErrlEntry::ADD_SW_CALLOUT);

            PLDM_ERR("sendrecv_pldm_request_impl: msg_sendrecv failed (rc=%d, PLID=0x%08x)",
                     return_code, ERRL_GETPLID_SAFE(errl));

            errl->collectTrace(PLDM_COMP_NAME);
            break;
        }
        else if (msg->data[1] != REQ_SUCCESS)
        {
            errl = reinterpret_cast<errlHndl_t>(msg->extra_data);
            msg->extra_data = nullptr;
            break;
        }

        // we own the memory pointed to by msg->extra_data
        o_response.reset(static_cast<uint8_t*>(msg->extra_data));
        o_response_length = msg->data[0];

        const size_t MIN_RESP_LEN = sizeof(pldm_msg_hdr) + 1;
        if (o_response_length < MIN_RESP_LEN)
        {
            auto hdr64 = pldmHdrToUint64(*reinterpret_cast<pldm_msg*>(o_response.get()));
            PLDM_ERR("The length of the response to request (0x%lx) was %d bytes when at least %d bytes was expected",
                     hdr64, o_response_length, MIN_RESP_LEN);

            /*@
             * @moduleid   MOD_MAKE_PLDM_REQUEST
             * @reasoncode RC_INVALID_LENGTH
             * @userdata1  Length of response in bytes
             * @userdata2  First bytes of PLDM header
             * @devdesc    Software problem, PLDM response message was too short
             * @custdesc   A software error occurred during system boot
             */
            errl = new ErrlEntry(ERRL_SEV_UNRECOVERABLE,
                                 MOD_MAKE_PLDM_REQUEST,
                                 RC_INVALID_LENGTH,
                                 o_response_length,
                                 hdr64);
            addBmcErrorCallouts(errl);
            break;
        }
    } while (false);

    return errl;
}

#endif
<|endoftext|>


<commit_before><commit_msg>Cálculo del mapa de disparidad reducido en función independiente y cambios menores<commit_after><|endoftext|>


<commit_before>// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "common/util/bijective_map.h"

#include <string>

#include "common/strings/compare.h"
#include "common/util/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace verible {
namespace {

using TestMapType = BijectiveMap<std::string, int>;

TEST(BijectiveMapTest, DefaultCtor) {
  const TestMapType m;
  EXPECT_EQ(m.size(), 0);
  EXPECT_TRUE(m.empty());
  EXPECT_FALSE(m.find_forward("a"));
  EXPECT_FALSE(m.find_reverse(1));
  EXPECT_TRUE(m.forward_view().empty());
  EXPECT_TRUE(m.reverse_view().empty());
}

TEST(BijectiveMapTest, InitializerListCtor) {
  const TestMapType m{{{"www", 2}, {"qqq", 3}}};
  EXPECT_EQ(m.size(), 2);
  EXPECT_FALSE(m.empty());
  EXPECT_FALSE(m.find_forward("a"));
  EXPECT_FALSE(m.find_reverse(1));
  EXPECT_EQ(*m.find_forward("qqq"), 3);
  EXPECT_EQ(*m.find_reverse(2), "www");
  EXPECT_FALSE(m.forward_view().empty());
  EXPECT_FALSE(m.reverse_view().empty());
}

TEST(BijectiveMapTest, InitializerListCtorDupeKey) {
  EXPECT_DEATH(const TestMapType m({{"www", 2}, {"www", 3}}), "duplicate");
}

TEST(BijectiveMapTest, InitializerListCtorDupeValue) {
  EXPECT_DEATH(const TestMapType m({{"www", 2}, {"qqq", 2}}), "duplicate");
}

TEST(BijectiveMapTest, IteratorPairCtor) {
  const std::initializer_list<std::pair<std::string, int>> values = {
      {"qqq", 3}, {"www", 2}};
  const TestMapType m(values.begin(), values.end());
  EXPECT_EQ(m.size(), 2);
  EXPECT_FALSE(m.empty());
  EXPECT_FALSE(m.find_forward("a"));
  EXPECT_FALSE(m.find_reverse(1));
  EXPECT_EQ(*m.find_forward("qqq"), 3);
  EXPECT_EQ(*m.find_reverse(2), "www");
  EXPECT_FALSE(m.forward_view().empty());
  EXPECT_FALSE(m.reverse_view().empty());
}

TEST(BijectiveMapTest, IteratorPairCtorDupeKey) {
  const std::initializer_list<std::pair<std::string, int>> values = {
      {"qqq", 4}, {"qqq", 2}};
  EXPECT_DEATH(const TestMapType m(values.begin(), values.end()), "duplicate");
}

TEST(BijectiveMapTest, IteratorPairCtorDupeValue) {
  const std::initializer_list<std::pair<std::string, int>> values = {
      {"zzz", 3}, {"qqq", 3}};
  EXPECT_DEATH(const TestMapType m(values.begin(), values.end()), "duplicate");
}

TEST(BijectiveMapTest, OneElement) {
  TestMapType m;
  EXPECT_TRUE(m.insert("a", 1));
  EXPECT_EQ(m.size(), 1);
  EXPECT_FALSE(m.empty());
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward("a")), 1);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(1)), "a");
  EXPECT_EQ(m.find_forward("b"), nullptr);
  EXPECT_EQ(m.find_reverse(2), nullptr);
}

TEST(BijectiveMapTest, InsertPair) {
  TestMapType m;
  EXPECT_TRUE(m.insert(std::make_pair("b", 2)));
  EXPECT_EQ(m.size(), 1);
  EXPECT_FALSE(m.empty());
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward("b")), 2);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(2)), "b");
  EXPECT_EQ(m.find_forward("a"), nullptr);
  EXPECT_EQ(m.find_reverse(1), nullptr);
}

TEST(BijectiveMapTest, InsertKeyAlreadyExists) {
  TestMapType m;
  EXPECT_TRUE(m.insert("a", 1));
  EXPECT_FALSE(m.insert("a", 3));
  EXPECT_EQ(m.size(), 1);
}

TEST(BijectiveMapTest, InsertValueAlreadyExists) {
  TestMapType m;
  EXPECT_TRUE(m.insert("a", 1));
  EXPECT_FALSE(m.insert("z", 1));
  EXPECT_EQ(m.size(), 1);
}

TEST(BijectiveMapTest, InsertNewPairs) {
  TestMapType m;
  EXPECT_TRUE(m.insert("a", 1));
  EXPECT_TRUE(m.insert("z", 4));
  EXPECT_EQ(m.size(), 2);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward("a")), 1);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(1)), "a");
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward("z")), 4);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(4)), "z");
  EXPECT_EQ(m.find_forward("b"), nullptr);
  EXPECT_EQ(m.find_reverse(3), nullptr);
}

static std::function<int()> LoopGeneratorGenerator(
    const std::vector<int>& values) {
  return [=]() {  // fake random generator
    static const std::vector<int> v(values);
    static std::vector<int>::const_iterator iter = v.begin();
    if (iter == v.end()) {
      iter = v.begin();
    }  // loop
    return *iter++;
  };
}

TEST(BijectiveMapTest, InsertRandom) {
  TestMapType m;
  auto gen = LoopGeneratorGenerator({1, 1, 2, 2, 3, 3});
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("b", gen)), 1);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("b", gen)), 1);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("g", gen)), 2);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("g", gen)), 2);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("f", gen)), 3);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("f", gen)), 3);
}

#if 0  // TODO(b/152331548): make work with C++11
// Testing heterogenous lookup: internally stored std::string, supporting
// copy-less lookup with absl::string_view.
using StringMapType = BijectiveMap<std::string, std::string, StringViewCompare,
                                   StringViewCompare>;

TEST(BijectiveMapTest, HeterogeneousStringLookup) {
  StringMapType m;
  EXPECT_TRUE(m.insert("a", "G"));
  EXPECT_TRUE(m.insert("z", "Q"));
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward(absl::string_view("a"))), "G");
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(absl::string_view("G"))), "a");
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward(absl::string_view("z"))), "Q");
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(absl::string_view("Q"))), "z");
  EXPECT_EQ(m.find_forward(absl::string_view("b")), nullptr);
  EXPECT_EQ(m.find_reverse(absl::string_view("3")), nullptr);
}
#endif

}  // namespace
}  // namespace verible
<commit_msg>Reenable bijective map test (wouldn't work on C++11)<commit_after>// Copyright 2017-2020 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "common/util/bijective_map.h"

#include <string>

#include "common/strings/compare.h"
#include "common/util/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"

namespace verible {
namespace {

using TestMapType = BijectiveMap<std::string, int>;

TEST(BijectiveMapTest, DefaultCtor) {
  const TestMapType m;
  EXPECT_EQ(m.size(), 0);
  EXPECT_TRUE(m.empty());
  EXPECT_FALSE(m.find_forward("a"));
  EXPECT_FALSE(m.find_reverse(1));
  EXPECT_TRUE(m.forward_view().empty());
  EXPECT_TRUE(m.reverse_view().empty());
}

TEST(BijectiveMapTest, InitializerListCtor) {
  const TestMapType m{{{"www", 2}, {"qqq", 3}}};
  EXPECT_EQ(m.size(), 2);
  EXPECT_FALSE(m.empty());
  EXPECT_FALSE(m.find_forward("a"));
  EXPECT_FALSE(m.find_reverse(1));
  EXPECT_EQ(*m.find_forward("qqq"), 3);
  EXPECT_EQ(*m.find_reverse(2), "www");
  EXPECT_FALSE(m.forward_view().empty());
  EXPECT_FALSE(m.reverse_view().empty());
}

TEST(BijectiveMapTest, InitializerListCtorDupeKey) {
  EXPECT_DEATH(const TestMapType m({{"www", 2}, {"www", 3}}), "duplicate");
}

TEST(BijectiveMapTest, InitializerListCtorDupeValue) {
  EXPECT_DEATH(const TestMapType m({{"www", 2}, {"qqq", 2}}), "duplicate");
}

TEST(BijectiveMapTest, IteratorPairCtor) {
  const std::initializer_list<std::pair<std::string, int>> values = {
      {"qqq", 3}, {"www", 2}};
  const TestMapType m(values.begin(), values.end());
  EXPECT_EQ(m.size(), 2);
  EXPECT_FALSE(m.empty());
  EXPECT_FALSE(m.find_forward("a"));
  EXPECT_FALSE(m.find_reverse(1));
  EXPECT_EQ(*m.find_forward("qqq"), 3);
  EXPECT_EQ(*m.find_reverse(2), "www");
  EXPECT_FALSE(m.forward_view().empty());
  EXPECT_FALSE(m.reverse_view().empty());
}

TEST(BijectiveMapTest, IteratorPairCtorDupeKey) {
  const std::initializer_list<std::pair<std::string, int>> values = {
      {"qqq", 4}, {"qqq", 2}};
  EXPECT_DEATH(const TestMapType m(values.begin(), values.end()), "duplicate");
}

TEST(BijectiveMapTest, IteratorPairCtorDupeValue) {
  const std::initializer_list<std::pair<std::string, int>> values = {
      {"zzz", 3}, {"qqq", 3}};
  EXPECT_DEATH(const TestMapType m(values.begin(), values.end()), "duplicate");
}

TEST(BijectiveMapTest, OneElement) {
  TestMapType m;
  EXPECT_TRUE(m.insert("a", 1));
  EXPECT_EQ(m.size(), 1);
  EXPECT_FALSE(m.empty());
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward("a")), 1);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(1)), "a");
  EXPECT_EQ(m.find_forward("b"), nullptr);
  EXPECT_EQ(m.find_reverse(2), nullptr);
}

TEST(BijectiveMapTest, InsertPair) {
  TestMapType m;
  EXPECT_TRUE(m.insert(std::make_pair("b", 2)));
  EXPECT_EQ(m.size(), 1);
  EXPECT_FALSE(m.empty());
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward("b")), 2);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(2)), "b");
  EXPECT_EQ(m.find_forward("a"), nullptr);
  EXPECT_EQ(m.find_reverse(1), nullptr);
}

TEST(BijectiveMapTest, InsertKeyAlreadyExists) {
  TestMapType m;
  EXPECT_TRUE(m.insert("a", 1));
  EXPECT_FALSE(m.insert("a", 3));
  EXPECT_EQ(m.size(), 1);
}

TEST(BijectiveMapTest, InsertValueAlreadyExists) {
  TestMapType m;
  EXPECT_TRUE(m.insert("a", 1));
  EXPECT_FALSE(m.insert("z", 1));
  EXPECT_EQ(m.size(), 1);
}

TEST(BijectiveMapTest, InsertNewPairs) {
  TestMapType m;
  EXPECT_TRUE(m.insert("a", 1));
  EXPECT_TRUE(m.insert("z", 4));
  EXPECT_EQ(m.size(), 2);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward("a")), 1);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(1)), "a");
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward("z")), 4);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(4)), "z");
  EXPECT_EQ(m.find_forward("b"), nullptr);
  EXPECT_EQ(m.find_reverse(3), nullptr);
}

static std::function<int()> LoopGeneratorGenerator(
    const std::vector<int>& values) {
  return [=]() {  // fake random generator
    static const std::vector<int> v(values);
    static std::vector<int>::const_iterator iter = v.begin();
    if (iter == v.end()) {
      iter = v.begin();
    }  // loop
    return *iter++;
  };
}

TEST(BijectiveMapTest, InsertRandom) {
  TestMapType m;
  auto gen = LoopGeneratorGenerator({1, 1, 2, 2, 3, 3});
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("b", gen)), 1);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("b", gen)), 1);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("g", gen)), 2);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("g", gen)), 2);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("f", gen)), 3);
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.insert_using_value_generator("f", gen)), 3);
}

// Testing heterogenous lookup: internally stored std::string, supporting
// copy-less lookup with absl::string_view.
using StringMapType = BijectiveMap<std::string, std::string, StringViewCompare,
                                   StringViewCompare>;

TEST(BijectiveMapTest, HeterogeneousStringLookup) {
  StringMapType m;
  EXPECT_TRUE(m.insert("a", "G"));
  EXPECT_TRUE(m.insert("z", "Q"));
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward(absl::string_view("a"))), "G");
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(absl::string_view("G"))), "a");
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_forward(absl::string_view("z"))), "Q");
  EXPECT_EQ(*ABSL_DIE_IF_NULL(m.find_reverse(absl::string_view("Q"))), "z");
  EXPECT_EQ(m.find_forward(absl::string_view("b")), nullptr);
  EXPECT_EQ(m.find_reverse(absl::string_view("3")), nullptr);
}

}  // namespace
}  // namespace verible
<|endoftext|>