<commit_before>/*
 * FogLAMP storage service.
 *
 * Copyright (c) 2018 OSisoft, LLC
 *
 * Released under the Apache 2.0 Licence
 *
 * Author: Mark Riddoch, Massimiliano Pinto
 */
#include <south_service.h>
#include <management_api.h>
#include <storage_client.h>
#include <service_record.h>
#include <plugin_manager.h>
#include <plugin_api.h>
#include <plugin.h>
#include <logger.h>
#include <reading.h>
#include <ingest.h>
#include <iostream>
#include <defaults.h>
#include <filter_plugin.h>

extern int makeDaemon(void);

using namespace std;

/**
 * South service main entry point
 */
int main(int argc, char *argv[])
{
unsigned short corePort = 8082;
string	       coreAddress = "localhost";
bool	       daemonMode = true;
string	       myName = SERVICE_NAME;

	for (int i = 1; i < argc; i++)
	{
		if (!strcmp(argv[i], "-d"))
		{
			daemonMode = false;
		}
		else if (!strncmp(argv[i], "--port=", 7))
		{
			corePort = (unsigned short)atoi(&argv[i][7]);
		}
		else if (!strncmp(argv[i], "--name=", 7))
		{
			myName = &argv[i][7];
		}
		else if (!strncmp(argv[i], "--address=", 10))
		{
			coreAddress = &argv[i][10];
		}
	}

	if (daemonMode && makeDaemon() == -1)
	{
		// Failed to run in daemon mode
		cout << "Failed to run as deamon - proceeding in interactive mode." << endl;
	}

	SouthService *service = new SouthService(myName);
	service->start(coreAddress, corePort);
	return 0;
}

/**
 * Detach the process from the terminal and run in the background.
 */
int makeDaemon()
{
pid_t pid;

	/* create new process */
	if ((pid = fork()  ) == -1)
	{
		return -1;
	}
	else if (pid != 0)
	{
		exit (EXIT_SUCCESS);
	}

	// If we got here we are a child process

	// create new session and process group
	if (setsid() == -1)
	{
		return -1;
	}

	// Close stdin, stdout and stderr
	close(0);
	close(1);
	close(2);
	// redirect fd's 0,1,2 to /dev/null
	(void)open("/dev/null", O_RDWR);  	// stdin
	(void)dup(0);  			// stdout	GCC bug 66425 produces warning
	(void)dup(0);  			// stderr	GCC bug 66425 produces warning
 	return 0;
}

/**
 * Constructor for the south service
 */
SouthService::SouthService(const string& myName) : m_name(myName), m_shutdown(false), m_pollInterval(1000)
{
	logger = new Logger(myName);
}

/**
 * Start the south service
 */
void SouthService::start(string& coreAddress, unsigned short corePort)
{
	unsigned short managementPort = (unsigned short)0;
	ManagementApi management(SERVICE_NAME, managementPort);	// Start managemenrt API
	logger->info("Starting south service...");
	management.registerService(this);

	// Listen for incomming managment requests
	management.start();

	// Allow time for the listeners to start before we register
	sleep(1);
	if (! m_shutdown)
	{
		// Now register our service
		// TODO proper hostname lookup
		unsigned short managementListener = management.getListenerPort();
		ServiceRecord record(m_name, "Southbound", "http", "localhost", 0, managementListener);
		m_mgtClient = new ManagementClient(coreAddress, corePort);

		m_config = m_mgtClient->getCategory(m_name);
		if (!loadPlugin())
		{
			logger->fatal("Failed to load south plugin.");
			return;
		}
		if (!m_mgtClient->registerService(record))
		{
			logger->error("Failed to register service %s", m_name.c_str());
		}
		unsigned int retryCount = 0;
		while (m_mgtClient->registerCategory(m_name) == false && ++retryCount < 10)
		{
			sleep(2 * retryCount);
		}

		// Get a handle on the storage layer
		ServiceRecord storageRecord("FogLAMP%20Storage");
		if (!m_mgtClient->getService(storageRecord))
		{
			logger->fatal("Unable to find storage service");
			return;
		}
		logger->info("Connect to storage on %s:%d",
				storageRecord.getAddress().c_str(),
				storageRecord.getPort());

		StorageClient storage(storageRecord.getAddress(),
						storageRecord.getPort());
		unsigned int threshold = 100;
		unsigned long timeout = 5000;
		try {
			if (m_config.itemExists("bufferThreshold"))
				threshold = (unsigned int)atoi(m_config.getValue("bufferThreshold").c_str());
			if (m_config.itemExists("maxSendLatency"))
				timeout = (unsigned long)atoi(m_config.getValue("maxSendLatency").c_str());
		} catch (ConfigItemNotFound e) {
			logger->info("Defaulting to inline defaults for south configuration");
		}

		// Instantiate the Ingest class
		Ingest ingest(storage, timeout, threshold);

		try {
			m_pollInterval = 500;
			if (m_config.itemExists("pollInterval"))
				m_pollInterval = (unsigned long)atoi(m_config.getValue("pollInterval").c_str());
		} catch (ConfigItemNotFound e) {
			logger->info("Defaulting to inline default for poll interval");
		}

		// Load filter plugins and set them in the Ingest class
		if (!this->loadFilters(m_name, ingest))
		{
			string errMsg("'" + m_name + "' plugin: failed loading filter plugins.");
			Logger::getLogger()->fatal((errMsg + " Exiting.").c_str());
			throw runtime_error(errMsg);
		}

		// Get and ingest data
		while (!m_shutdown)
		{
			std::this_thread::sleep_for(std::chrono::milliseconds(m_pollInterval));
			Reading reading = southPlugin->poll();
			ingest.ingest(reading);
		}

		// Clean shutdown, unregister the storage service
		m_mgtClient->unregisterService();
	}
	logger->info("South service shut down.");
}

/**
 * Stop the storage service/
 */
void SouthService::stop()
{
	logger->info("Stopping south service...\n");
}

/**
 * Load the configured south plugin
 *
 * TODO Should search for the plugin in specified locations
 */
bool SouthService::loadPlugin()
{
	try {
		PluginManager *manager = PluginManager::getInstance();

		if (! m_config.itemExists("plugin"))
		{
			logger->error("Unable to fetch plugin name from configuration.\n");
			return false;
		}
		string plugin = m_config.getValue("plugin");
		logger->info("Loading south plugin %s.", plugin.c_str());
		PLUGIN_HANDLE handle;
		if ((handle = manager->loadPlugin(plugin, PLUGIN_TYPE_SOUTH)) != NULL)
		{
			// Deal with registering and fetching the configuration
			DefaultConfigCategory defConfig(plugin, manager->getInfo(handle)->config);
			addConfigDefaults(defConfig);
			defConfig.setDescription(m_config.getDescription());

			// Create/Update category name (we pass keep_original_items=true)
			m_mgtClient->addCategory(defConfig, true);

			// Must now relaod the configuration to obtain any items added from
			// the plugin
			m_config = m_mgtClient->getCategory(m_name);

			southPlugin = new SouthPlugin(handle, m_config);
			logger->info("Loaded south plugin %s.", plugin.c_str());
			return true;
		}
	} catch (exception e) {
		logger->fatal("Failed to load south plugin: %s\n", e.what());
	}
	return false;
}

/**
 * Shutdown request
 */
void SouthService::shutdown()
{
	/* Stop recieving new requests and allow existing
	 * requests to drain.
	 */
	m_shutdown = true;
	logger->info("South service shutdown in progress.");
}

/**
 * Configuration change notification
 */
void SouthService::configChange(const string& categoryName, const string& category)
{
	// TODO action configuration change
	logger->info("Configuration change in category %s: %s", categoryName.c_str(),
			category.c_str());
	m_config = m_mgtClient->getCategory(m_name);
	try {
		m_pollInterval = (unsigned long)atoi(m_config.getValue("pollInterval").c_str());
	} catch (ConfigItemNotFound e) {
		logger->error("Failed to update poll interval following configuration change");
	}
}

/**
 * Add the generic south service configuration options to the default retrieved
 * from the specific plugin.
 *
 * @param defaultConfiguration	The default configuration from the plugin
 */
void SouthService::addConfigDefaults(DefaultConfigCategory& defaultConfig)
{
	for (int i = 0; defaults[i].name; i++)
	{
		defaultConfig.addItem(defaults[i].name, defaults[i].description,
			defaults[i].type, defaults[i].value, defaults[i].value);
	}
}

/**
 * Load filter plugins
 *
 * Filters found in configuration are loaded
 * and adde to the Ingest class instance
 *
 * @param categoryName	Configuration category name
 * @param ingest	The Ingest class reference
 *			Filters are added to m_filters member
 * @return true		True if all filters ihave been loaded
 *			or not existing filters.
 *			False for errors.
 */
bool SouthService::loadFilters(const string& categoryName,
			       Ingest& ingest)
{
	// Try to load filters:
	if (!FilterPlugin::loadFilters(categoryName,
				       ingest.m_filters,
				       m_mgtClient))
	{
		// Return false on any error
		return false;
	}

	if (ingest.m_filters.size())
	{
		// We have some filters: set up the filter pipeline
		this->setupFiltersPipeline(ingest);
	}

	return true;
}

/**
 * Set the filterPipeline in the Ingest class
 *
 * This method calls the the method "plugin_init" for all loadade filters.
 * Up to date filter configurations and Ingest filtering methods
 * are passed to "plugin_init"
 *
 * @param ingest	The ingest class
 */
void SouthService::setupFiltersPipeline(const Ingest& ingest) const
{
	for (auto it = ingest.m_filters.begin(); it != ingest.m_filters.end(); ++it)
	{
		string filterCategoryName = m_name;
		filterCategoryName.append("_");
		filterCategoryName += (*it)->getName();
		filterCategoryName.append("Filter");

		ConfigCategory updatedCfg;
		vector<string> children;

		try
		{
			// Fetch up to date filter configuration
			updatedCfg = m_mgtClient->getCategory(filterCategoryName);

			// Add filter category name under service/process config name
			children.push_back(filterCategoryName);
			m_mgtClient->addChildCategories(m_name, children);
		}
		// TODO catch specific exceptions
		catch (...)
		{
			throw;
		}

		// Iterate the load filters set in the Ingest class m_filters member
		if ((it + 1) != ingest.m_filters.end())
		{
			// Set next filter pointer as OUTPUT_HANDLE
			(*it)->init(updatedCfg,
				    (OUTPUT_HANDLE *)(*(it + 1)),
				    Ingest::passToOnwardFilter);
		}
		else
		{
			// Set the Ingest class pointer as OUTPUT_HANDLE
			(*it)->init(updatedCfg,
				    (OUTPUT_HANDLE *)&ingest,
				    Ingest::useFilteredData);
		}
	}
}
<commit_msg>FOGL-1536: fix compilation error (#984)<commit_after>/*
 * FogLAMP storage service.
 *
 * Copyright (c) 2018 OSisoft, LLC
 *
 * Released under the Apache 2.0 Licence
 *
 * Author: Mark Riddoch, Massimiliano Pinto
 */
#include <south_service.h>
#include <management_api.h>
#include <storage_client.h>
#include <service_record.h>
#include <plugin_manager.h>
#include <plugin_api.h>
#include <plugin.h>
#include <logger.h>
#include <reading.h>
#include <ingest.h>
#include <iostream>
#include <defaults.h>
#include <filter_plugin.h>

extern int makeDaemon(void);

using namespace std;

/**
 * South service main entry point
 */
int main(int argc, char *argv[])
{
unsigned short corePort = 8082;
string	       coreAddress = "localhost";
bool	       daemonMode = true;
string	       myName = SERVICE_NAME;

	for (int i = 1; i < argc; i++)
	{
		if (!strcmp(argv[i], "-d"))
		{
			daemonMode = false;
		}
		else if (!strncmp(argv[i], "--port=", 7))
		{
			corePort = (unsigned short)atoi(&argv[i][7]);
		}
		else if (!strncmp(argv[i], "--name=", 7))
		{
			myName = &argv[i][7];
		}
		else if (!strncmp(argv[i], "--address=", 10))
		{
			coreAddress = &argv[i][10];
		}
	}

	if (daemonMode && makeDaemon() == -1)
	{
		// Failed to run in daemon mode
		cout << "Failed to run as deamon - proceeding in interactive mode." << endl;
	}

	SouthService *service = new SouthService(myName);
	service->start(coreAddress, corePort);
	return 0;
}

/**
 * Detach the process from the terminal and run in the background.
 */
int makeDaemon()
{
pid_t pid;

	/* create new process */
	if ((pid = fork()  ) == -1)
	{
		return -1;
	}
	else if (pid != 0)
	{
		exit (EXIT_SUCCESS);
	}

	// If we got here we are a child process

	// create new session and process group
	if (setsid() == -1)
	{
		return -1;
	}

	// Close stdin, stdout and stderr
	close(0);
	close(1);
	close(2);
	// redirect fd's 0,1,2 to /dev/null
	(void)open("/dev/null", O_RDWR);  	// stdin
	(void)dup(0);  			// stdout	GCC bug 66425 produces warning
	(void)dup(0);  			// stderr	GCC bug 66425 produces warning
 	return 0;
}

/**
 * Constructor for the south service
 */
SouthService::SouthService(const string& myName) : m_name(myName), m_shutdown(false), m_pollInterval(1000)
{
	logger = new Logger(myName);
}

/**
 * Start the south service
 */
void SouthService::start(string& coreAddress, unsigned short corePort)
{
	unsigned short managementPort = (unsigned short)0;
	ManagementApi management(SERVICE_NAME, managementPort);	// Start managemenrt API
	logger->info("Starting south service...");
	management.registerService(this);

	// Listen for incomming managment requests
	management.start();

	// Allow time for the listeners to start before we register
	sleep(1);
	if (! m_shutdown)
	{
		// Now register our service
		// TODO proper hostname lookup
		unsigned short managementListener = management.getListenerPort();
		ServiceRecord record(m_name, "Southbound", "http", "localhost", 0, managementListener);
		m_mgtClient = new ManagementClient(coreAddress, corePort);

		m_config = m_mgtClient->getCategory(m_name);
		if (!loadPlugin())
		{
			logger->fatal("Failed to load south plugin.");
			return;
		}
		if (!m_mgtClient->registerService(record))
		{
			logger->error("Failed to register service %s", m_name.c_str());
		}
		unsigned int retryCount = 0;
		while (m_mgtClient->registerCategory(m_name) == false && ++retryCount < 10)
		{
			sleep(2 * retryCount);
		}

		// Get a handle on the storage layer
		ServiceRecord storageRecord("FogLAMP%20Storage");
		if (!m_mgtClient->getService(storageRecord))
		{
			logger->fatal("Unable to find storage service");
			return;
		}
		logger->info("Connect to storage on %s:%d",
				storageRecord.getAddress().c_str(),
				storageRecord.getPort());

		StorageClient storage(storageRecord.getAddress(),
						storageRecord.getPort());
		unsigned int threshold = 100;
		unsigned long timeout = 5000;
		try {
			if (m_config.itemExists("bufferThreshold"))
				threshold = (unsigned int)atoi(m_config.getValue("bufferThreshold").c_str());
			if (m_config.itemExists("maxSendLatency"))
				timeout = (unsigned long)atoi(m_config.getValue("maxSendLatency").c_str());
		} catch (ConfigItemNotFound e) {
			logger->info("Defaulting to inline defaults for south configuration");
		}

		// Instantiate the Ingest class
		Ingest ingest(storage, timeout, threshold);

		try {
			m_pollInterval = 500;
			if (m_config.itemExists("pollInterval"))
				m_pollInterval = (unsigned long)atoi(m_config.getValue("pollInterval").c_str());
		} catch (ConfigItemNotFound e) {
			logger->info("Defaulting to inline default for poll interval");
		}

		// Load filter plugins and set them in the Ingest class
		if (!this->loadFilters(m_name, ingest))
		{
			string errMsg("'" + m_name + "' plugin: failed loading filter plugins.");
			Logger::getLogger()->fatal((errMsg + " Exiting.").c_str());
			throw runtime_error(errMsg);
		}

		// Get and ingest data
		while (!m_shutdown)
		{
			std::this_thread::sleep_for(std::chrono::milliseconds(m_pollInterval));
			Reading reading = southPlugin->poll();
			ingest.ingest(reading);
		}

		// Clean shutdown, unregister the storage service
		m_mgtClient->unregisterService();
	}
	logger->info("South service shut down.");
}

/**
 * Stop the storage service/
 */
void SouthService::stop()
{
	logger->info("Stopping south service...\n");
}

/**
 * Load the configured south plugin
 *
 * TODO Should search for the plugin in specified locations
 */
bool SouthService::loadPlugin()
{
	try {
		PluginManager *manager = PluginManager::getInstance();

		if (! m_config.itemExists("plugin"))
		{
			logger->error("Unable to fetch plugin name from configuration.\n");
			return false;
		}
		string plugin = m_config.getValue("plugin");
		logger->info("Loading south plugin %s.", plugin.c_str());
		PLUGIN_HANDLE handle;
		if ((handle = manager->loadPlugin(plugin, PLUGIN_TYPE_SOUTH)) != NULL)
		{
			// Deal with registering and fetching the configuration
			DefaultConfigCategory defConfig(plugin, manager->getInfo(handle)->config);
			addConfigDefaults(defConfig);
			defConfig.setDescription(m_config.getDescription());

			// Create/Update category name (we pass keep_original_items=true)
			m_mgtClient->addCategory(defConfig, true);

			// Must now relaod the configuration to obtain any items added from
			// the plugin
			m_config = m_mgtClient->getCategory(m_name);

			southPlugin = new SouthPlugin(handle, m_config);
			logger->info("Loaded south plugin %s.", plugin.c_str());
			return true;
		}
	} catch (exception e) {
		logger->fatal("Failed to load south plugin: %s\n", e.what());
	}
	return false;
}

/**
 * Shutdown request
 */
void SouthService::shutdown()
{
	/* Stop recieving new requests and allow existing
	 * requests to drain.
	 */
	m_shutdown = true;
	logger->info("South service shutdown in progress.");
}

/**
 * Configuration change notification
 */
void SouthService::configChange(const string& categoryName, const string& category)
{
	// TODO action configuration change
	logger->info("Configuration change in category %s: %s", categoryName.c_str(),
			category.c_str());
	m_config = m_mgtClient->getCategory(m_name);
	try {
		m_pollInterval = (unsigned long)atoi(m_config.getValue("pollInterval").c_str());
	} catch (ConfigItemNotFound e) {
		logger->error("Failed to update poll interval following configuration change");
	}
}

/**
 * Add the generic south service configuration options to the default retrieved
 * from the specific plugin.
 *
 * @param defaultConfiguration	The default configuration from the plugin
 */
void SouthService::addConfigDefaults(DefaultConfigCategory& defaultConfig)
{
	for (int i = 0; defaults[i].name; i++)
	{
		defaultConfig.addItem(defaults[i].name, defaults[i].description,
			defaults[i].type, defaults[i].value, defaults[i].value);
	}
}

/**
 * Load filter plugins
 *
 * Filters found in configuration are loaded
 * and adde to the Ingest class instance
 *
 * @param categoryName	Configuration category name
 * @param ingest	The Ingest class reference
 *			Filters are added to m_filters member
 * @return true		True if all filters ihave been loaded
 *			or not existing filters.
 *			False for errors.
 */
bool SouthService::loadFilters(const string& categoryName,
			       Ingest& ingest) const
{
	// Try to load filters:
	if (!FilterPlugin::loadFilters(categoryName,
				       ingest.m_filters,
				       m_mgtClient))
	{
		// Return false on any error
		return false;
	}

	// Set up the filter pipeline
	this->setupFiltersPipeline(ingest);

	return true;
}

/**
 * Set the filterPipeline in the Ingest class
 *
 * This method calls the the method "plugin_init" for all loadade filters.
 * Up to date filter configurations and Ingest filtering methods
 * are passed to "plugin_init"
 *
 * @param ingest	The ingest class
 */
void SouthService::setupFiltersPipeline(const Ingest& ingest) const
{
	for (auto it = ingest.m_filters.begin(); it != ingest.m_filters.end(); ++it)
	{
		string filterCategoryName = m_name;
		filterCategoryName.append("_");
		filterCategoryName += (*it)->getName();
		filterCategoryName.append("Filter");

		ConfigCategory updatedCfg;
		vector<string> children;

		try
		{
			// Fetch up to date filter configuration
			updatedCfg = m_mgtClient->getCategory(filterCategoryName);

			// Add filter category name under service/process config name
			children.push_back(filterCategoryName);
			m_mgtClient->addChildCategories(m_name, children);
		}
		// TODO catch specific exceptions
		catch (...)
		{
			throw;
		}

		// Iterate the load filters set in the Ingest class m_filters member
		if ((it + 1) != ingest.m_filters.end())
		{
			// Set next filter pointer as OUTPUT_HANDLE
			(*it)->init(updatedCfg,
				    (OUTPUT_HANDLE *)(*(it + 1)),
				    Ingest::passToOnwardFilter);
		}
		else
		{
			// Set the Ingest class pointer as OUTPUT_HANDLE
			(*it)->init(updatedCfg,
				    (OUTPUT_HANDLE *)&ingest,
				    Ingest::useFilteredData);
		}
	}
}
<|endoftext|>


<commit_before>#include "thread.h"

namespace Util{

namespace Thread{

LockObject::LockObject(){
    initializeLock(&lock);
    initializeCondition(&condition);
    // Global::debug(0) << "Created lock " << lock << std::endl;
    // Global::debug(0) << "Created condition " << condition << std::endl;
}

int LockObject::acquire() const {
    /* quick hack to get around annoying constness */
    return acquireLock((Lock*) &lock);
}

int LockObject::release() const {
    return releaseLock((Lock*) &lock);
}

void LockObject::wait() const {
    int ok = 1;
    while (ok != 0){
        /* if conditionWait succeeds then ok will be 0 */
        ok = conditionWait((Condition*) &condition, (Lock*) &lock);
    }
}

void LockObject::wait(volatile bool & check) const {
    int ok = 0;
    /* only wait if check is false. if so then keep waiting until the condition
     * was successful as well.
     */
    while (!check || ok != 0){
        if (ok != 0){
            // Global::debug(0) << "Wait failed: " << SDL_GetError() << std::endl;
        }
        ok = conditionWait((Condition*) &condition, (Lock*) &lock);
    }
}

void LockObject::signal() const {
    conditionSignal((Condition*) &condition);
}

void LockObject::lockAndSignal(volatile bool & check, bool what) const {
    acquire();
    check = what;
    signal();
    release();
}

LockObject::~LockObject(){
    destroyLock(&lock);
    destroyCondition(&condition);
}

ScopedLock::ScopedLock(const LockObject & lock):
lock(lock){
    lock.acquire();
}

ScopedLock::~ScopedLock(){
    lock.release();
}

bool isUninitialized(Id thread){
    return thread == uninitializedValue;
}

ThreadObject::ThreadObject(void * data, void * (function)(void * arg)):
data(data),
function(function),
thread(uninitializedValue){
}

bool ThreadObject::start(){
    if (thread == uninitializedValue){
        return createThread(&thread, NULL, (Thread::ThreadFunction) function, data);
    } else {
        return false;
    }
}

ThreadObject::~ThreadObject(){
    if (thread != uninitializedValue){
        joinThread(thread);
        thread = uninitializedValue;
    }
}

#if defined(USE_SDL) && !defined(USE_NACL)
Id uninitializedValue = NULL;

bool initializeLock(Lock * lock){
    *lock = SDL_CreateMutex();
    return *lock != NULL;
}

int acquireLock(Lock * lock){
    return SDL_LockMutex(*lock);
}

int releaseLock(Lock * lock){
    return SDL_UnlockMutex(*lock);
}

void destroyLock(Lock * lock){
    SDL_DestroyMutex(*lock);
}

void initializeCondition(Condition * condition){
    *condition = SDL_CreateCond();
    if (condition == NULL){
        Global::debug(0) << "Could not create condition" << std::endl;
    }
}

void destroyCondition(Condition * condition){
    SDL_DestroyCond(*condition);
}

int conditionWait(Condition * condition, Lock * lock){
    return SDL_CondWait(*condition, *lock);
}

int conditionSignal(Condition * condition){
    return SDL_CondBroadcast(*condition);
}

/*
void initializeSemaphore(Semaphore * semaphore, unsigned int value){
    *semaphore = SDL_CreateSemaphore(value);
}

void destroySemaphore(Semaphore * semaphore){
    SDL_DestroySemaphore(*semaphore);
}

void semaphoreDecrease(Semaphore * semaphore){
    SDL_SemWait(*semaphore);
}

void semaphoreIncrease(Semaphore * semaphore){
    SDL_SemPost(*semaphore);
}
*/

bool createThread(Id * thread, void * attributes, ThreadFunction function, void * arg){
    *thread = SDL_CreateThread(function, arg);
    return *thread != NULL;
}

void joinThread(Id thread){
    if (!isUninitialized(thread)){
        SDL_WaitThread(thread, NULL);
    }
}

void cancelThread(Id thread){
#if !SDL_VERSION_ATLEAST(1, 3, 0)
    SDL_KillThread(thread);
#endif
}

#elif USE_ALLEGRO5
Id uninitializedValue = 0;

bool initializeLock(Lock * lock){
    *lock = al_create_mutex();
    return *lock != NULL;
}

int acquireLock(Lock * lock){
    al_lock_mutex(*lock);
    return 0;
}

int releaseLock(Lock * lock){
    al_unlock_mutex(*lock);
    return 0;
}

void initializeCondition(Condition * condition){
    *condition = al_create_cond();
}

void destroyCondition(Condition * condition){
    al_destroy_cond(*condition);
}

int conditionWait(Condition * condition, Lock * lock){
    al_wait_cond(*condition, *lock);
    return 0;
}

int conditionSignal(Condition * condition){
    al_broadcast_cond(*condition);
    return 0;
}

struct AllegroThreadStuff{
    AllegroThreadStuff(const ThreadFunction & function, void * arg):
    function(function),
    arg(arg){
    }

    ThreadFunction function;
    void * arg;
};

static void * allegro_start_thread(ALLEGRO_THREAD * self, void * _stuff){
    AllegroThreadStuff * stuff = (AllegroThreadStuff*) _stuff;
    ThreadFunction function = stuff->function;
    void * arg = stuff->arg;
    delete stuff;
    return function(arg);
}

bool createThread(Id * thread, void * attributes, ThreadFunction function, void * arg){
    AllegroThreadStuff * stuff = new AllegroThreadStuff(function, arg);
    *thread = al_create_thread(allegro_start_thread, stuff);
    if (*thread != NULL){
        al_start_thread(*thread);
        return true;
    } else {
        delete stuff;
        return false;
    }
}

void joinThread(Id thread){
    if (!isUninitialized(thread)){
        al_join_thread(thread, NULL);
    }
}

void cancelThread(Id thread){
    al_destroy_thread(thread);
}

void destroyLock(Lock * lock){
    al_destroy_mutex(*lock);
}

#else
Id uninitializedValue = 0;

bool initializeLock(Lock * lock){
    return pthread_mutex_init(lock, NULL) == 0;
}

int acquireLock(Lock * lock){
    return pthread_mutex_lock(lock);
}

int releaseLock(Lock * lock){
    return pthread_mutex_unlock(lock);
}

void initializeCondition(Condition * condition){
    pthread_cond_init(condition, NULL);
}

void destroyCondition(Condition * condition){
    pthread_cond_destroy(condition);
}

int conditionWait(Condition * condition, Lock * lock){
    return pthread_cond_wait(condition, lock);
}

int conditionSignal(Condition * condition){
    return pthread_cond_broadcast(condition);
}

#if 0
void initializeSemaphore(Semaphore * semaphore, unsigned int value){
    sem_init(semaphore, 0, value);
}

void destroySemaphore(Semaphore * semaphore){
    /* nothing */
}

void semaphoreDecrease(Semaphore * semaphore){
    sem_wait(semaphore);
}

void semaphoreIncrease(Semaphore * semaphore){
    sem_post(semaphore);
}
#endif

bool createThread(Id * thread, void * attributes, ThreadFunction function, void * arg){
    return pthread_create(thread, (pthread_attr_t*) attributes, function, arg) == 0;
}

void joinThread(Id thread){
    if (!isUninitialized(thread)){
        pthread_join(thread, NULL);
    }
}

void cancelThread(Id thread){
    /* FIXME: cancel is not implemented for libogc, find another way.
     * thread suspend/resume is there, though.
     */
#if !defined(WII) && !defined(USE_NACL)
    pthread_cancel(thread);
#endif
}

void destroyLock(Lock * lock){
    /* nothing */
}

#endif

}

WaitThread::WaitThread():
done(false){
    Thread::initializeLock(&doneLock);
}

WaitThread::WaitThread(Thread::ThreadFunction thread, void * arg){
    Thread::initializeLock(&doneLock);
    start(thread, arg);
}

static void * do_thread(void * arg){
    WaitThread * thread = (WaitThread *) arg;
    thread->doRun();
    return NULL;
}

void WaitThread::doRun(){
    this->function(this->arg);

    Thread::acquireLock(&doneLock);
    this->done = true;
    Thread::releaseLock(&doneLock);
}

void WaitThread::start(Thread::ThreadFunction thread, void * arg){
    done = false;
    this->arg = arg;
    this->function = thread;
    Thread::createThread(&this->thread, NULL, (Thread::ThreadFunction) do_thread, this);
}

bool WaitThread::isRunning(){
    Thread::acquireLock(&doneLock);
    bool what = done;
    Thread::releaseLock(&doneLock);
    return what;
}

void WaitThread::kill(){
    Thread::cancelThread(thread);
    Thread::joinThread(thread);
}

WaitThread::~WaitThread(){
    /* FIXME: Should we join the thread? */
    /* pthread_join(thread); */
    Thread::joinThread(thread);
}

ThreadBoolean::ThreadBoolean(volatile bool & what, Thread::Lock & lock):
what(what),
lock(lock){
}

bool ThreadBoolean::get(){
    Thread::acquireLock(&lock);
    bool b = what;
    Thread::releaseLock(&lock);
    return b;
}

void ThreadBoolean::set(bool value){
    Thread::acquireLock(&lock);
    what = value;
    Thread::releaseLock(&lock);
}

}
<commit_msg>on the wii, use libogcs thread api directly<commit_after>#include "thread.h"

#ifdef WII
/* So we can call ogc's create thread directly */
#include <ogc/lwp.h>
#endif

namespace Util{

namespace Thread{

LockObject::LockObject(){
    initializeLock(&lock);
    initializeCondition(&condition);
    // Global::debug(0) << "Created lock " << lock << std::endl;
    // Global::debug(0) << "Created condition " << condition << std::endl;
}

int LockObject::acquire() const {
    /* quick hack to get around annoying constness */
    return acquireLock((Lock*) &lock);
}

int LockObject::release() const {
    return releaseLock((Lock*) &lock);
}

void LockObject::wait() const {
    int ok = 1;
    while (ok != 0){
        /* if conditionWait succeeds then ok will be 0 */
        ok = conditionWait((Condition*) &condition, (Lock*) &lock);
    }
}

void LockObject::wait(volatile bool & check) const {
    int ok = 0;
    /* only wait if check is false. if so then keep waiting until the condition
     * was successful as well.
     */
    while (!check || ok != 0){
        if (ok != 0){
            // Global::debug(0) << "Wait failed: " << SDL_GetError() << std::endl;
        }
        ok = conditionWait((Condition*) &condition, (Lock*) &lock);
    }
}

void LockObject::signal() const {
    conditionSignal((Condition*) &condition);
}

void LockObject::lockAndSignal(volatile bool & check, bool what) const {
    acquire();
    check = what;
    signal();
    release();
}

LockObject::~LockObject(){
    destroyLock(&lock);
    destroyCondition(&condition);
}

ScopedLock::ScopedLock(const LockObject & lock):
lock(lock){
    lock.acquire();
}

ScopedLock::~ScopedLock(){
    lock.release();
}

bool isUninitialized(Id thread){
    return thread == uninitializedValue;
}

ThreadObject::ThreadObject(void * data, void * (function)(void * arg)):
data(data),
function(function),
thread(uninitializedValue){
}

bool ThreadObject::start(){
    if (thread == uninitializedValue){
        return createThread(&thread, NULL, (Thread::ThreadFunction) function, data);
    } else {
        return false;
    }
}

ThreadObject::~ThreadObject(){
    if (thread != uninitializedValue){
        joinThread(thread);
        thread = uninitializedValue;
    }
}

#if defined(USE_SDL) && !defined(USE_NACL)
Id uninitializedValue = NULL;

bool initializeLock(Lock * lock){
    *lock = SDL_CreateMutex();
    return *lock != NULL;
}

int acquireLock(Lock * lock){
    return SDL_LockMutex(*lock);
}

int releaseLock(Lock * lock){
    return SDL_UnlockMutex(*lock);
}

void destroyLock(Lock * lock){
    SDL_DestroyMutex(*lock);
}

void initializeCondition(Condition * condition){
    *condition = SDL_CreateCond();
    if (condition == NULL){
        Global::debug(0) << "Could not create condition" << std::endl;
    }
}

void destroyCondition(Condition * condition){
    SDL_DestroyCond(*condition);
}

int conditionWait(Condition * condition, Lock * lock){
    return SDL_CondWait(*condition, *lock);
}

int conditionSignal(Condition * condition){
    return SDL_CondBroadcast(*condition);
}

/*
void initializeSemaphore(Semaphore * semaphore, unsigned int value){
    *semaphore = SDL_CreateSemaphore(value);
}

void destroySemaphore(Semaphore * semaphore){
    SDL_DestroySemaphore(*semaphore);
}

void semaphoreDecrease(Semaphore * semaphore){
    SDL_SemWait(*semaphore);
}

void semaphoreIncrease(Semaphore * semaphore){
    SDL_SemPost(*semaphore);
}
*/

#ifdef WII
struct LwpThread{
    lwp_t thread;
    char * stack;
};

/* We use libogc's LWP_CreateThread directly because SDL's thread api doesnt let us
 * explicitly specify the stack size.
 * MUGEN in particular needs a relatively large stack (somewhere around 32kb-64kb)
 * to run the parsers. We allocate a little more than that just to be safe (128kb).
 * It is easier to use libogc directly than hack SDL each time.
 */
bool wiiCreateThread(Id * thread, void * attributes, ThreadFunction function, void * arg){
    LwpThread * lwp = new LwpThread();
    int size = 128 * 1024;
    lwp->stack = new char[size];
    if (LWP_CreateThread(&lwp->thread, (void * (*)(void*)) function, arg, lwp->stack, size, 80) != 0){
        delete[] lwp->stack;
        delete lwp;
        return false;
    }
    *thread = (Id) lwp;
    return true;
}

void wiiJoinThread(Id thread){
    LwpThread * lwp = (LwpThread*) thread;
    if (LWP_JoinThread(lwp->thread, NULL) == 0){
        delete[] lwp->stack;
        delete lwp;
    } else {
        /* Now what?? */
    }
}
#endif

bool createThread(Id * thread, void * attributes, ThreadFunction function, void * arg){
#ifdef WII
    return wiiCreateThread(thread, attributes, function, arg);
#else
    *thread = SDL_CreateThread(function, arg);
    return *thread != NULL;
#endif
}

void joinThread(Id thread){
    if (!isUninitialized(thread)){
#ifdef WII
        wiiJoinThread(thread);
#else
        SDL_WaitThread(thread, NULL);
#endif
    }
}

void cancelThread(Id thread){
#if !SDL_VERSION_ATLEAST(1, 3, 0)
    SDL_KillThread(thread);
#endif
}

#elif USE_ALLEGRO5
Id uninitializedValue = 0;

bool initializeLock(Lock * lock){
    *lock = al_create_mutex();
    return *lock != NULL;
}

int acquireLock(Lock * lock){
    al_lock_mutex(*lock);
    return 0;
}

int releaseLock(Lock * lock){
    al_unlock_mutex(*lock);
    return 0;
}

void initializeCondition(Condition * condition){
    *condition = al_create_cond();
}

void destroyCondition(Condition * condition){
    al_destroy_cond(*condition);
}

int conditionWait(Condition * condition, Lock * lock){
    al_wait_cond(*condition, *lock);
    return 0;
}

int conditionSignal(Condition * condition){
    al_broadcast_cond(*condition);
    return 0;
}

struct AllegroThreadStuff{
    AllegroThreadStuff(const ThreadFunction & function, void * arg):
    function(function),
    arg(arg){
    }

    ThreadFunction function;
    void * arg;
};

static void * allegro_start_thread(ALLEGRO_THREAD * self, void * _stuff){
    AllegroThreadStuff * stuff = (AllegroThreadStuff*) _stuff;
    ThreadFunction function = stuff->function;
    void * arg = stuff->arg;
    delete stuff;
    return function(arg);
}

bool createThread(Id * thread, void * attributes, ThreadFunction function, void * arg){
    AllegroThreadStuff * stuff = new AllegroThreadStuff(function, arg);
    *thread = al_create_thread(allegro_start_thread, stuff);
    if (*thread != NULL){
        al_start_thread(*thread);
        return true;
    } else {
        delete stuff;
        return false;
    }
}

void joinThread(Id thread){
    if (!isUninitialized(thread)){
        al_join_thread(thread, NULL);
    }
}

void cancelThread(Id thread){
    al_destroy_thread(thread);
}

void destroyLock(Lock * lock){
    al_destroy_mutex(*lock);
}

#else
Id uninitializedValue = 0;

bool initializeLock(Lock * lock){
    return pthread_mutex_init(lock, NULL) == 0;
}

int acquireLock(Lock * lock){
    return pthread_mutex_lock(lock);
}

int releaseLock(Lock * lock){
    return pthread_mutex_unlock(lock);
}

void initializeCondition(Condition * condition){
    pthread_cond_init(condition, NULL);
}

void destroyCondition(Condition * condition){
    pthread_cond_destroy(condition);
}

int conditionWait(Condition * condition, Lock * lock){
    return pthread_cond_wait(condition, lock);
}

int conditionSignal(Condition * condition){
    return pthread_cond_broadcast(condition);
}

#if 0
void initializeSemaphore(Semaphore * semaphore, unsigned int value){
    sem_init(semaphore, 0, value);
}

void destroySemaphore(Semaphore * semaphore){
    /* nothing */
}

void semaphoreDecrease(Semaphore * semaphore){
    sem_wait(semaphore);
}

void semaphoreIncrease(Semaphore * semaphore){
    sem_post(semaphore);
}
#endif

bool createThread(Id * thread, void * attributes, ThreadFunction function, void * arg){
    return pthread_create(thread, (pthread_attr_t*) attributes, function, arg) == 0;
}

void joinThread(Id thread){
    if (!isUninitialized(thread)){
        pthread_join(thread, NULL);
    }
}

void cancelThread(Id thread){
    /* FIXME: cancel is not implemented for libogc, find another way.
     * thread suspend/resume is there, though.
     */
#if !defined(WII) && !defined(USE_NACL)
    pthread_cancel(thread);
#endif
}

void destroyLock(Lock * lock){
    /* nothing */
}

#endif

}

WaitThread::WaitThread():
done(false){
    Thread::initializeLock(&doneLock);
}

WaitThread::WaitThread(Thread::ThreadFunction thread, void * arg){
    Thread::initializeLock(&doneLock);
    start(thread, arg);
}

static void * do_thread(void * arg){
    WaitThread * thread = (WaitThread *) arg;
    thread->doRun();
    return NULL;
}

void WaitThread::doRun(){
    this->function(this->arg);

    Thread::acquireLock(&doneLock);
    this->done = true;
    Thread::releaseLock(&doneLock);
}

void WaitThread::start(Thread::ThreadFunction thread, void * arg){
    done = false;
    this->arg = arg;
    this->function = thread;
    Thread::createThread(&this->thread, NULL, (Thread::ThreadFunction) do_thread, this);
}

bool WaitThread::isRunning(){
    Thread::acquireLock(&doneLock);
    bool what = done;
    Thread::releaseLock(&doneLock);
    return what;
}

void WaitThread::kill(){
    Thread::cancelThread(thread);
    Thread::joinThread(thread);
}

WaitThread::~WaitThread(){
    /* FIXME: Should we join the thread? */
    /* pthread_join(thread); */
    Thread::joinThread(thread);
}

ThreadBoolean::ThreadBoolean(volatile bool & what, Thread::Lock & lock):
what(what),
lock(lock){
}

bool ThreadBoolean::get(){
    Thread::acquireLock(&lock);
    bool b = what;
    Thread::releaseLock(&lock);
    return b;
}

void ThreadBoolean::set(bool value){
    Thread::acquireLock(&lock);
    what = value;
    Thread::releaseLock(&lock);
}

}
<|endoftext|>


<commit_before>#include <sys/un.h>

#include "utils/bspwm.hpp"
#include "utils/env.hpp"

POLYBAR_NS

namespace bspwm_util {
  /**
   * Get all bspwm root windows
   */
  vector<xcb_window_t> root_windows(connection& conn) {
    vector<xcb_window_t> roots;
    auto children = conn.query_tree(conn.screen()->root).children();

    for (auto it = children.begin(); it != children.end(); it++) {
      xcb_icccm_get_wm_class_reply_t reply;
      reply.class_name = reply.instance_name = nullptr;

      if (xcb_icccm_get_wm_class_reply(conn, xcb_icccm_get_wm_class(conn, *it), &reply, nullptr)) {
        if (string_util::compare("Bspwm", reply.class_name) && string_util::compare("root", reply.instance_name)) {
          roots.emplace_back(*it);
        }
      }

      if (reply.class_name != nullptr || reply.instance_name != nullptr) {
        xcb_icccm_get_wm_class_reply_wipe(&reply);
      }
    }

    return roots;
  }

  /**
   * Restack given window above the bspwm root window
   * for the given monitor.
   *
   * Fixes the issue with always-on-top window's
   */
  bool restack_above_root(connection& conn, const monitor_t& mon, const xcb_window_t win) {
    for (auto&& root : root_windows(conn)) {
      auto geom = conn.get_geometry(root);

      if (mon->x != geom->x || mon->y != geom->y)
        continue;
      if (mon->w != geom->width || mon->h != geom->height)
        continue;

      const uint32_t value_mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
      const uint32_t value_list[2]{root, XCB_STACK_MODE_ABOVE};

      conn.configure_window_checked(win, value_mask, value_list);
      conn.flush();

      return true;
    }

    return false;
  }

  /**
   * Get path to the bspwm socket by the following order
   *
   * 1. Value of environment variable BSPWM_SOCKET
   * 2. Value built from the bspwm socket path template
   * 3. Value of the macro BSPWM_SOCKET_PATH
   */
  string get_socket_path() {
    string env_path;

    if ((env_path = env_util::get("BSPWM_SOCKET")).empty() == false)
      return env_path;

    struct sockaddr_un sa;
    char* host = nullptr;
    int dsp = 0;
    int scr = 0;

    if (xcb_parse_display(nullptr, &host, &dsp, &scr) == 0)
      return BSPWM_SOCKET_PATH;

    snprintf(sa.sun_path, sizeof(sa.sun_path), "/tmp/bspwm%s_%i_%i-socket", host, dsp, scr);

    return sa.sun_path;
  }

  /**
   * Generate a payload object with properly formatted data
   * ready to be sent to the bspwm ipc controller
   */
  payload_t make_payload(string cmd) {
    payload_t payload{new payload_t::element_type{}};
    auto size = sizeof(payload->data);
    int offset = 0;
    int chars = 0;

    for (auto&& word : string_util::split(cmd, ' ')) {
      chars = snprintf(payload->data + offset, size - offset, "%s%c", word.c_str(), 0);
      payload->len += chars;
      offset += chars;
    }

    return payload;
  }

  /**
   * Create an ipc socket connection
   *
   * Example usage:
   * @code cpp
   *   auto ipc = make_connection();
   *   ipc->send(make_payload("desktop -f eDP-1:^1"));
   * @endcode
   */
  connection_t make_connection() {
    return socket_util::make_unix_connection(get_socket_path());
  }

  /**
   * Create a connection and subscribe to events
   * on the bspwm socket
   *
   * Example usage:
   * @code cpp
   *   auto ipc = make_subscriber();
   *
   *   while (!ipc->poll(POLLHUP, 0)) {
   *     ssize_t bytes_received = 0;
   *     auto data = ipc->receive(BUFSIZ-1, bytes_received, 0);
   *     std::cout << data << std::endl;
   *   }
   * @endcode
   */
  connection_t make_subscriber() {
    auto conn = make_connection();
    auto payload = make_payload("subscribe report");
    if (conn->send(payload->data, payload->len, 0) == 0)
      throw system_error("Failed to initialize subscriber");
    return conn;
  }
}

POLYBAR_NS_END
<commit_msg>fix(bspwm_util): Memory leak<commit_after>#include <sys/un.h>

#include "utils/bspwm.hpp"
#include "utils/env.hpp"

POLYBAR_NS

namespace bspwm_util {
  /**
   * Get all bspwm root windows
   */
  vector<xcb_window_t> root_windows(connection& conn) {
    vector<xcb_window_t> roots;
    auto children = conn.query_tree(conn.screen()->root).children();

    for (auto it = children.begin(); it != children.end(); it++) {
      xcb_icccm_get_wm_class_reply_t reply;
      reply.class_name = reply.instance_name = nullptr;

      if (xcb_icccm_get_wm_class_reply(conn, xcb_icccm_get_wm_class(conn, *it), &reply, nullptr)) {
        if (string_util::compare("Bspwm", reply.class_name) && string_util::compare("root", reply.instance_name)) {
          roots.emplace_back(*it);
        }
      }

      if (reply.class_name != nullptr || reply.instance_name != nullptr) {
        xcb_icccm_get_wm_class_reply_wipe(&reply);
      }
    }

    return roots;
  }

  /**
   * Restack given window above the bspwm root window
   * for the given monitor.
   *
   * Fixes the issue with always-on-top window's
   */
  bool restack_above_root(connection& conn, const monitor_t& mon, const xcb_window_t win) {
    for (auto&& root : root_windows(conn)) {
      auto geom = conn.get_geometry(root);

      if (mon->x != geom->x || mon->y != geom->y)
        continue;
      if (mon->w != geom->width || mon->h != geom->height)
        continue;

      const uint32_t value_mask = XCB_CONFIG_WINDOW_SIBLING | XCB_CONFIG_WINDOW_STACK_MODE;
      const uint32_t value_list[2]{root, XCB_STACK_MODE_ABOVE};

      conn.configure_window_checked(win, value_mask, value_list);
      conn.flush();

      return true;
    }

    return false;
  }

  /**
   * Get path to the bspwm socket by the following order
   *
   * 1. Value of environment variable BSPWM_SOCKET
   * 2. Value built from the bspwm socket path template
   * 3. Value of the macro BSPWM_SOCKET_PATH
   */
  string get_socket_path() {
    string env_path;

    if ((env_path = env_util::get("BSPWM_SOCKET")).empty() == false)
      return env_path;

    struct sockaddr_un sa;
    char* host = nullptr;
    int dsp = 0;
    int scr = 0;

    if (xcb_parse_display(nullptr, &host, &dsp, &scr) == 0)
      return BSPWM_SOCKET_PATH;

    snprintf(sa.sun_path, sizeof(sa.sun_path), "/tmp/bspwm%s_%i_%i-socket", host, dsp, scr);
    free(host);

    return sa.sun_path;
  }

  /**
   * Generate a payload object with properly formatted data
   * ready to be sent to the bspwm ipc controller
   */
  payload_t make_payload(string cmd) {
    payload_t payload{new payload_t::element_type{}};
    auto size = sizeof(payload->data);
    int offset = 0;
    int chars = 0;

    for (auto&& word : string_util::split(cmd, ' ')) {
      chars = snprintf(payload->data + offset, size - offset, "%s%c", word.c_str(), 0);
      payload->len += chars;
      offset += chars;
    }

    return payload;
  }

  /**
   * Create an ipc socket connection
   *
   * Example usage:
   * @code cpp
   *   auto ipc = make_connection();
   *   ipc->send(make_payload("desktop -f eDP-1:^1"));
   * @endcode
   */
  connection_t make_connection() {
    return socket_util::make_unix_connection(get_socket_path());
  }

  /**
   * Create a connection and subscribe to events
   * on the bspwm socket
   *
   * Example usage:
   * @code cpp
   *   auto ipc = make_subscriber();
   *
   *   while (!ipc->poll(POLLHUP, 0)) {
   *     ssize_t bytes_received = 0;
   *     auto data = ipc->receive(BUFSIZ-1, bytes_received, 0);
   *     std::cout << data << std::endl;
   *   }
   * @endcode
   */
  connection_t make_subscriber() {
    auto conn = make_connection();
    auto payload = make_payload("subscribe report");
    if (conn->send(payload->data, payload->len, 0) == 0)
      throw system_error("Failed to initialize subscriber");
    return conn;
  }
}

POLYBAR_NS_END
<|endoftext|>


<commit_before>// Copyright (c) 2014, Alexey Ivanov

#include "stdafx.h"
#include "image.h"
#include <boost/noncopyable.hpp>
#include "utils/util.h"

namespace ImageUtils
{
namespace FreeImage
{

/* Adapter for std::vector<BYTE> to save/load into it by FreeImage library. */
class FIIOVectorHandle : boost::noncopyable
{
public:
    typedef std::vector<BYTE> ImageDataType;

    FIIOVectorHandle(ImageDataType& image_data)
        :
        cur_position_(0), image_data_(image_data)
    {}

    /* Allow implicit cast to fi_handle. */
    operator fi_handle()
    {
        return this;
    }

    static unsigned DLL_CALLCONV ReadProc(void* buffer, unsigned size, unsigned count, fi_handle handle)
    {
        FIIOVectorHandle* handle_ = static_cast<FIIOVectorHandle*>(handle);
        unsigned x;
        for (x = 0; x < count; ++x) {
            //if there isnt size bytes left to read, set pos to eof and return a short count
            if( handle_->image_data_.size() - handle_->cur_position_ < size ) {
                handle_->cur_position_ = handle_->image_data_.size();
                break;
            }
            //copy size bytes count times
            memcpy(buffer, &(handle_->image_data_[0]) + handle_->cur_position_, size);
            handle_->cur_position_ += size;
            buffer = static_cast<char*>(buffer) + size;
        }
        return x;
    }

    static unsigned DLL_CALLCONV WriteProc(void* buffer, unsigned size, unsigned count, fi_handle handle)
    {
        typedef FIIOVectorHandle::ImageDataType::size_type SizeType;
        FIIOVectorHandle* handle_ = static_cast<FIIOVectorHandle*>(handle);

        //double the data block size if we need to
        SizeType required_image_data_size = handle_->cur_position_ + (size * count);
        if ( required_image_data_size >= handle_->image_data_.capacity() ) {

            SizeType current_image_data_size = handle_->image_data_.size(); // cash current image data size
            SizeType new_capacity;
            //if we are at or above 1G, we cant double without going negative
            if (current_image_data_size & 0x40000000) {
                //max 2G
                if (current_image_data_size == 0x7FFFFFFF) {
                    return 0;
                }
                new_capacity = 0x7FFFFFFF;
            } else if (current_image_data_size == 0) {
                //default to 4K if nothing yet
                new_capacity = 4096;
            } else {
                //double size
                new_capacity = required_image_data_size << 1;
            }

            handle_->image_data_.reserve(new_capacity);
        }

        handle_->image_data_.insert( handle_->image_data_.end(),
                                     static_cast<BYTE*>(buffer),
                                     static_cast<BYTE*>(buffer) + size * count);
        handle_->cur_position_ += (size * count);
        return count;
    }

    static int DLL_CALLCONV SeekProc(fi_handle handle, long offset, int origin)
    {
        FIIOVectorHandle* handle_ = static_cast<FIIOVectorHandle*>(handle);
        switch (origin) // 0 to filelen-1 are 'inside' the file
        {
        default:
        case SEEK_SET: // can fseek() to 0-7FFFFFFF always
            if (offset >= 0) {
                handle_->cur_position_ = offset;
                return 0;
            }
            break;

        case SEEK_CUR:
        {
            INT64 new_position = static_cast<INT64>(handle_->cur_position_) + offset;
            if ( 0 <= new_position && new_position <= handle_->image_data_.size() ) {
                handle_->cur_position_ += offset;
                return 0;
            }
            break;
        }

        case SEEK_END:
            if (offset <= 0) {
                INT64 new_position = static_cast<INT64>(handle_->image_data_.size()) + offset;
                if (new_position >= 0) {
                    handle_->cur_position_ = static_cast<FIIOVectorHandle::ImageDataType::size_type>(new_position);
                    return 0;
                }
            }
            break;
        }

        return -1;
    }

    static long DLL_CALLCONV TellProc(fi_handle handle)
    {
        return static_cast<FIIOVectorHandle*>(handle)->cur_position_;
    }

    /* Initialize FreeImageIO struct. */
    void InitFreeImageIO(FreeImageIO* io)
    {
        io->read_proc  = FIIOVectorHandle::ReadProc;
        io->seek_proc  = FIIOVectorHandle::SeekProc;
        io->tell_proc  = FIIOVectorHandle::TellProc;
        io->write_proc = FIIOVectorHandle::WriteProc;
    }

private:

    ImageDataType::size_type cur_position_;
    ImageDataType& image_data_;
};

//// use for store data about error in FreeImage library.
//std::string free_library_last_error_message;

/* Function used to get error description from FreeImage library, see FreeImage_SetOutputMessage description.
    Depends on FreeImage.lib and commented.
*/
//void FreeImageErrorHandler(FREE_IMAGE_FORMAT fif, const char *message) {
//    std::ostringstream msg;
//    if (fif != FIF_UNKNOWN) {
//        msg << FreeImage_GetFormatFromFIF(fif) << " format. ";
//    }
//    msg << message;
//    free_library_last_error_message = msg.str();
//}

} // namespace FreeImage

//! Get size of bitmap by it's handle.
SIZE getBitmapSize(HBITMAP bitmap_handle)
{
    BITMAP bitmap;
    int errorcode = GetObject(bitmap_handle, sizeof(bitmap), &bitmap);
    if (0 == errorcode) {
        throw std::runtime_error("Failed to get size of bitmap.");
    }

    SIZE bitmap_size = { bitmap.bmWidth, bitmap.bmHeight };
    return bitmap_size;
}

//! general tempate for convinient casting. Provide specialization for your own types.
template<typename To, typename From> To cast(From);

/*!
    \brief Converts image format ID from AIMPCoverImage to FREE_IMAGE_FORMAT.
    If AIMPCoverImage format is unknown returns FIF_UNKNOWN format.
*/
template<>
FREE_IMAGE_FORMAT cast<FREE_IMAGE_FORMAT, IMAGEFORMAT>(IMAGEFORMAT image_format)
{
    FREE_IMAGE_FORMAT freeimage_format = FIF_UNKNOWN;
    switch (image_format)
    {
    case PNG_IMAGE:
        freeimage_format = FIF_PNG;
        break;
    case BMP_IMAGE:
        freeimage_format = FIF_BMP;
        break;
    case JPEG_IMAGE:
        freeimage_format = FIF_JPEG;
        break;
    }
    return freeimage_format;
}

struct GDIDeleter : boost::noncopyable
{
    GDIDeleter(HGDIOBJ obj, bool need_delete = true)
        : obj_(obj), need_delete_(need_delete)
    {}

    ~GDIDeleter()
    {
        if (need_delete_) {
            DeleteObject(obj_);
        }
    }

    HGDIOBJ obj_;
    bool need_delete_;

private:
    GDIDeleter();
};

AIMPCoverImage::AIMPCoverImage(HBITMAP cover_bitmap_handle, bool release_bitmap, unsigned width, unsigned height) // throws std::runtime_error
{
    GDIDeleter deleter(cover_bitmap_handle, release_bitmap);

    BOOL result = copyFromBitmap(cover_bitmap_handle);

    if (FALSE == result) {
        throw std::runtime_error("Error occured while create AIMPCoverImage from HBITMAP.");
    }

    if (width != 0 && height != 0) {
        SIZE size = { width, height };
        SIZE bitmap_size = getBitmapSize(cover_bitmap_handle);
        if (bitmap_size.cx != size.cx || bitmap_size.cy != size.cy) {
            result = rescale(size.cx, size.cy, FREE_IMAGE_FILTER::FILTER_BICUBIC);
            if (FALSE == result) {
                using namespace Utilities;
                throw std::runtime_error(MakeString() << "Error occured while rescaling image to (" << size.cx << ", " << size.cy << ").");
            }
        }
    }

    result = convertTo24Bits();
    if (FALSE == result) {
        throw std::runtime_error("Error occured while converting AIMPCoverImage to 24bpp.");
    }
}

void AIMPCoverImage::saveToVector(IMAGEFORMAT image_format, std::vector<BYTE>& image_data) const // throws std::runtime_error
{
    using namespace FreeImage;

    FIIOVectorHandle memory_handle(image_data);
    FreeImageIO io;
    memory_handle.InitFreeImageIO(&io);

    //FreeImage_SetOutputMessage(FreeImage::FreeImageErrorHandler); // set handler that fill FreeImage::free_library_last_error_message string in case of error.
    BOOL result = saveToHandle(cast<FREE_IMAGE_FORMAT>(image_format), &io, &memory_handle);
    //FreeImage_SetOutputMessage(nullptr); // remove error handler.
    if (FALSE == result) {
        using namespace Utilities;
        throw std::runtime_error(MakeString() << "Error occured while saving image in vector. Reason: " << "unknown"); // << FreeImage::free_library_last_error_message;
    }
}

void AIMPCoverImage::saveToFile(const std::wstring& file_name) const // throws std::runtime_error
{
    //FreeImage_SetOutputMessage(FreeImage::FreeImageErrorHandler); // set handler that fill FreeImage::free_library_last_error_message string in case of error.
    BOOL result = saveU(file_name.c_str(), 0); // 0 means default saving settings.
    //using namespace FreeImage;
    //free_library_last_error_message = free_library_last_error_message;
    //FreeImage_SetOutputMessage(nullptr); // remove error handler.

    if (FALSE == result) {
        using namespace Utilities;
        throw std::runtime_error(MakeString() << "Error occured while saving image to file. Reason: " << "unknown"); // << FreeImage::free_library_last_error_message;
    }
}

} // namespace ImageUtils
<commit_msg>fix warning C4838: conversion from 'unsigned int' to 'LONG' requires a narrowing conversion<commit_after>// Copyright (c) 2014, Alexey Ivanov

#include "stdafx.h"
#include "image.h"
#include <boost/noncopyable.hpp>
#include "utils/util.h"

namespace ImageUtils
{
namespace FreeImage
{

/* Adapter for std::vector<BYTE> to save/load into it by FreeImage library. */
class FIIOVectorHandle : boost::noncopyable
{
public:
    typedef std::vector<BYTE> ImageDataType;

    FIIOVectorHandle(ImageDataType& image_data)
        :
        cur_position_(0), image_data_(image_data)
    {}

    /* Allow implicit cast to fi_handle. */
    operator fi_handle()
    {
        return this;
    }

    static unsigned DLL_CALLCONV ReadProc(void* buffer, unsigned size, unsigned count, fi_handle handle)
    {
        FIIOVectorHandle* handle_ = static_cast<FIIOVectorHandle*>(handle);
        unsigned x;
        for (x = 0; x < count; ++x) {
            //if there isnt size bytes left to read, set pos to eof and return a short count
            if( handle_->image_data_.size() - handle_->cur_position_ < size ) {
                handle_->cur_position_ = handle_->image_data_.size();
                break;
            }
            //copy size bytes count times
            memcpy(buffer, &(handle_->image_data_[0]) + handle_->cur_position_, size);
            handle_->cur_position_ += size;
            buffer = static_cast<char*>(buffer) + size;
        }
        return x;
    }

    static unsigned DLL_CALLCONV WriteProc(void* buffer, unsigned size, unsigned count, fi_handle handle)
    {
        typedef FIIOVectorHandle::ImageDataType::size_type SizeType;
        FIIOVectorHandle* handle_ = static_cast<FIIOVectorHandle*>(handle);

        //double the data block size if we need to
        SizeType required_image_data_size = handle_->cur_position_ + (size * count);
        if ( required_image_data_size >= handle_->image_data_.capacity() ) {

            SizeType current_image_data_size = handle_->image_data_.size(); // cash current image data size
            SizeType new_capacity;
            //if we are at or above 1G, we cant double without going negative
            if (current_image_data_size & 0x40000000) {
                //max 2G
                if (current_image_data_size == 0x7FFFFFFF) {
                    return 0;
                }
                new_capacity = 0x7FFFFFFF;
            } else if (current_image_data_size == 0) {
                //default to 4K if nothing yet
                new_capacity = 4096;
            } else {
                //double size
                new_capacity = required_image_data_size << 1;
            }

            handle_->image_data_.reserve(new_capacity);
        }

        handle_->image_data_.insert( handle_->image_data_.end(),
                                     static_cast<BYTE*>(buffer),
                                     static_cast<BYTE*>(buffer) + size * count);
        handle_->cur_position_ += (size * count);
        return count;
    }

    static int DLL_CALLCONV SeekProc(fi_handle handle, long offset, int origin)
    {
        FIIOVectorHandle* handle_ = static_cast<FIIOVectorHandle*>(handle);
        switch (origin) // 0 to filelen-1 are 'inside' the file
        {
        default:
        case SEEK_SET: // can fseek() to 0-7FFFFFFF always
            if (offset >= 0) {
                handle_->cur_position_ = offset;
                return 0;
            }
            break;

        case SEEK_CUR:
        {
            INT64 new_position = static_cast<INT64>(handle_->cur_position_) + offset;
            if ( 0 <= new_position && new_position <= handle_->image_data_.size() ) {
                handle_->cur_position_ += offset;
                return 0;
            }
            break;
        }

        case SEEK_END:
            if (offset <= 0) {
                INT64 new_position = static_cast<INT64>(handle_->image_data_.size()) + offset;
                if (new_position >= 0) {
                    handle_->cur_position_ = static_cast<FIIOVectorHandle::ImageDataType::size_type>(new_position);
                    return 0;
                }
            }
            break;
        }

        return -1;
    }

    static long DLL_CALLCONV TellProc(fi_handle handle)
    {
        return static_cast<FIIOVectorHandle*>(handle)->cur_position_;
    }

    /* Initialize FreeImageIO struct. */
    void InitFreeImageIO(FreeImageIO* io)
    {
        io->read_proc  = FIIOVectorHandle::ReadProc;
        io->seek_proc  = FIIOVectorHandle::SeekProc;
        io->tell_proc  = FIIOVectorHandle::TellProc;
        io->write_proc = FIIOVectorHandle::WriteProc;
    }

private:

    ImageDataType::size_type cur_position_;
    ImageDataType& image_data_;
};

//// use for store data about error in FreeImage library.
//std::string free_library_last_error_message;

/* Function used to get error description from FreeImage library, see FreeImage_SetOutputMessage description.
    Depends on FreeImage.lib and commented.
*/
//void FreeImageErrorHandler(FREE_IMAGE_FORMAT fif, const char *message) {
//    std::ostringstream msg;
//    if (fif != FIF_UNKNOWN) {
//        msg << FreeImage_GetFormatFromFIF(fif) << " format. ";
//    }
//    msg << message;
//    free_library_last_error_message = msg.str();
//}

} // namespace FreeImage

//! Get size of bitmap by it's handle.
SIZE getBitmapSize(HBITMAP bitmap_handle)
{
    BITMAP bitmap;
    int errorcode = GetObject(bitmap_handle, sizeof(bitmap), &bitmap);
    if (0 == errorcode) {
        throw std::runtime_error("Failed to get size of bitmap.");
    }

    SIZE bitmap_size = { bitmap.bmWidth, bitmap.bmHeight };
    return bitmap_size;
}

//! general tempate for convinient casting. Provide specialization for your own types.
template<typename To, typename From> To cast(From);

/*!
    \brief Converts image format ID from AIMPCoverImage to FREE_IMAGE_FORMAT.
    If AIMPCoverImage format is unknown returns FIF_UNKNOWN format.
*/
template<>
FREE_IMAGE_FORMAT cast<FREE_IMAGE_FORMAT, IMAGEFORMAT>(IMAGEFORMAT image_format)
{
    FREE_IMAGE_FORMAT freeimage_format = FIF_UNKNOWN;
    switch (image_format)
    {
    case PNG_IMAGE:
        freeimage_format = FIF_PNG;
        break;
    case BMP_IMAGE:
        freeimage_format = FIF_BMP;
        break;
    case JPEG_IMAGE:
        freeimage_format = FIF_JPEG;
        break;
    }
    return freeimage_format;
}

struct GDIDeleter : boost::noncopyable
{
    GDIDeleter(HGDIOBJ obj, bool need_delete = true)
        : obj_(obj), need_delete_(need_delete)
    {}

    ~GDIDeleter()
    {
        if (need_delete_) {
            DeleteObject(obj_);
        }
    }

    HGDIOBJ obj_;
    bool need_delete_;

private:
    GDIDeleter();
};

AIMPCoverImage::AIMPCoverImage(HBITMAP cover_bitmap_handle, bool release_bitmap, unsigned width, unsigned height) // throws std::runtime_error
{
    GDIDeleter deleter(cover_bitmap_handle, release_bitmap);

    BOOL result = copyFromBitmap(cover_bitmap_handle);

    if (FALSE == result) {
        throw std::runtime_error("Error occured while create AIMPCoverImage from HBITMAP.");
    }

    if (	(0 < width && width <= (unsigned)std::numeric_limits<LONG>::max())
		&&	(0 < height && height <= (unsigned)std::numeric_limits<LONG>::max())
		)
	{
        SIZE size = { (LONG)width, (LONG)height };
        SIZE bitmap_size = getBitmapSize(cover_bitmap_handle);
        if (bitmap_size.cx != size.cx || bitmap_size.cy != size.cy) {
            result = rescale(size.cx, size.cy, FREE_IMAGE_FILTER::FILTER_BICUBIC);
            if (FALSE == result) {
                using namespace Utilities;
                throw std::runtime_error(MakeString() << "Error occured while rescaling image to (" << size.cx << ", " << size.cy << ").");
            }
        }
    }

    result = convertTo24Bits();
    if (FALSE == result) {
        throw std::runtime_error("Error occured while converting AIMPCoverImage to 24bpp.");
    }
}

void AIMPCoverImage::saveToVector(IMAGEFORMAT image_format, std::vector<BYTE>& image_data) const // throws std::runtime_error
{
    using namespace FreeImage;

    FIIOVectorHandle memory_handle(image_data);
    FreeImageIO io;
    memory_handle.InitFreeImageIO(&io);

    //FreeImage_SetOutputMessage(FreeImage::FreeImageErrorHandler); // set handler that fill FreeImage::free_library_last_error_message string in case of error.
    BOOL result = saveToHandle(cast<FREE_IMAGE_FORMAT>(image_format), &io, &memory_handle);
    //FreeImage_SetOutputMessage(nullptr); // remove error handler.
    if (FALSE == result) {
        using namespace Utilities;
        throw std::runtime_error(MakeString() << "Error occured while saving image in vector. Reason: " << "unknown"); // << FreeImage::free_library_last_error_message;
    }
}

void AIMPCoverImage::saveToFile(const std::wstring& file_name) const // throws std::runtime_error
{
    //FreeImage_SetOutputMessage(FreeImage::FreeImageErrorHandler); // set handler that fill FreeImage::free_library_last_error_message string in case of error.
    BOOL result = saveU(file_name.c_str(), 0); // 0 means default saving settings.
    //using namespace FreeImage;
    //free_library_last_error_message = free_library_last_error_message;
    //FreeImage_SetOutputMessage(nullptr); // remove error handler.

    if (FALSE == result) {
        using namespace Utilities;
        throw std::runtime_error(MakeString() << "Error occured while saving image to file. Reason: " << "unknown"); // << FreeImage::free_library_last_error_message;
    }
}

} // namespace ImageUtils
<|endoftext|>


<commit_before>/*
 * Copyright 2010, 2011 Esrille Inc.
 *
 * 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 "FontManager.h"

#include <algorithm>

#include <freetype/tttables.h>
#include <freetype/ftsizes.h>
#include <unicode/utypes.h>

#include "utf.h"
#include "TextIterator.h"

using namespace org::w3c::dom::bootstrap;

namespace {

// cf. CSSWhiteSpaceValueImp
enum {
    Normal,
    Pre,
    Nowrap,
    PreWrap,
    PreLine
};

bool isCollapsingSpace(unsigned value) {
    switch (value) {
    case Normal:
    case Nowrap:
    case PreLine:
        return true;
    default:
        return false;
    }
}

}
//
// FontManager
//

FontManager::~FontManager()
{
    for (std::map<std::string, FontFace*>::iterator it = faces.begin(); it != faces.end(); ++it)
        delete it->second;
    FT_Done_FreeType(library);
}

FontFace* FontManager::getFontFace(const std::string fontFilename) throw ()
{
    std::map<std::string, FontFace*>::iterator it = faces.find(fontFilename);
    if (it != faces.end())
        return it->second;
    FontFace* face = 0;
    try {
        face = new FontFace(this, fontFilename);
        faces.insert(std::pair<std::string, FontFace*>(fontFilename, face));
    } catch (...) {
        delete face;
        return 0;
    }
    return face;
}

//
// FontFace
//

FontFace::FontFace(FontManager* manager, const std::string fontFilename, long index) try :
    manager(manager),
    charmap(0)
{
    FT_Error error = FT_New_Face(manager->library, fontFilename.c_str(), index, &face);
    if (error) {
        face = 0;
        throw std::runtime_error(__func__);
    }
    initCharmap();
} catch (...) {
    if (face)
        FT_Done_Face(face);
    throw;
}

FontFace::~FontFace()
{
    for (std::map<unsigned int, FontTexture*>::iterator it = textures.begin(); it != textures.end(); ++it)
        delete it->second;
    FT_Done_Face(face);
}

FontTexture* FontFace::getFontTexture(unsigned int point)
{
    std::map<unsigned int, FontTexture*>::iterator it = textures.find(point);
    if (it != textures.end())
        return it->second;
    FontTexture* texture = 0;
    try {
        texture = new FontTexture(this, point);
        textures.insert(std::pair<unsigned int, FontTexture*>(point, texture));
    } catch (...) {
        delete texture;
        return 0;
    }
    return texture;
}

//
// FontTexture
//

FontTexture::FontTexture(FontFace* face, unsigned int point) try :
    face(face),
    glyphs(0),
    point(point),
    bearingGap(0.0f)
{
    addImage();
    glyphs = new FontGlyph[face->glyphCount];

    sizes[0] = face->face->size;
    for (int i = 1; i < Sizes; ++i) {
        FT_Error error = FT_New_Size(face->face, &sizes[i]);
        if (error)
            throw std::runtime_error(__func__);
    }

    unsigned px = (point * 96) / 72;  // TODO: make dpi configurable
    for (int i = 0; i < Sizes; ++i) {
        FT_Activate_Size(sizes[i]);
        FT_Error error = FT_Set_Pixel_Sizes(face->face, 0, px);
        if (error)
            throw std::runtime_error(__func__);
        px >>= 1;
    }
    FT_Activate_Size(sizes[0]);

    ascender = face->face->ascender;
    descender = face->face->descender;
    pen.x = pen.y = Offset;  // (0, 0) is reserved for an uninitialized font glyph
    ymax = 0;

    lineGap = 0;
    xHeight = ascender / 2;
    if (FT_IS_SFNT(face->face)) {
        if (TT_OS2_* os2 = static_cast<TT_OS2_*>(FT_Get_Sfnt_Table(face->face, ft_sfnt_os2))) {
            lineGap = os2->usWinAscent + os2->usWinDescent - face->face->units_per_EM;
            xHeight = os2->sxHeight;

            float b = (point * 96.0f) / 72.0f * 64.0f;
            b *= static_cast<float>(ascender) / (ascender - descender);
            b /= 64.0f;
            bearingGap = (roundf(b) - b) * 64.0f;
        }
    }

    // Store the missing glyph (0) as the 1st entry
    if (!storeGlyph(glyphs, 0))
        throw std::runtime_error(__func__);
} catch (...) {
    delete glyphs;
    throw;
}

FontTexture::~FontTexture()
{
    for (std::vector<uint8_t*>::iterator it = images.begin(); it != images.end(); ++it)
        deleteImage(*it);
    delete[] glyphs;
}

FontGlyph* FontTexture::getGlyph(int32_t ucode)
{
    std::vector<int32_t>::iterator result;
    result = std::lower_bound(face->charmap.begin(), face->charmap.end(), ucode);
    if (*result != ucode)
        return glyphs;
    FontGlyph* glyph = &glyphs[result - face->charmap.begin()];
    if (!glyph->isInitialized()) {
        FT_UInt glyphIndex = FT_Get_Char_Index(face->face, ucode);
        assert(glyphIndex);
        if (!glyphIndex)
            return glyphs;
        if (!storeGlyph(glyph, glyphIndex))
            return glyphs;
    }
    return glyph;
}

uint8_t* FontTexture::getImage(FontGlyph* glyph)
{
    // TODO: check range
    return images[glyph->y / Height];
}

bool FontTexture::storeGlyph(FontGlyph* glyph, FT_UInt glyphIndex)
{
    // load glyph image into the slot (erase previous one)
    FT_Error error = FT_Load_Glyph(face->face, glyphIndex, FT_LOAD_NO_HINTING);
    if (error)
        return false;

    // convert to an anti-aliased bitmap
    error = FT_Render_Glyph(face->face->glyph, FT_RENDER_MODE_NORMAL);
    if (error)
        return false;

    uint8_t* image;

    FT_GlyphSlot slot = face->face->glyph;
    try {
        image = drawBitmap(glyph, slot);
    } catch (...) {
        return false;
    }
    assert(glyph->isInitialized());

    // update mipmap textures.
    for (int i = 1; i < Sizes; ++i) {
        FT_Activate_Size(sizes[i]);
        // load glyph image into the slot (erase previous one)
        FT_Error error = FT_Load_Glyph(face->face, glyphIndex, FT_LOAD_NO_HINTING);
        if (error)
            return false;
        // convert to an anti-aliased bitmap
        error = FT_Render_Glyph(face->face->glyph, FT_RENDER_MODE_NORMAL);
        if (error)
            return false;
        FT_GlyphSlot slot = face->face->glyph;
        try {
            drawBitmap(glyph, slot, i);
        } catch (...) {
            return false;
        }
    }
    FT_Activate_Size(sizes[0]);

    updateImage(image, glyph);

    return true;
}

uint8_t* FontTexture::drawBitmap(FontGlyph* glyph, FT_GlyphSlot slot)
{
    uint8_t* image = images.back();

    FT_Bitmap* bitmap = &slot->bitmap;
    unsigned w = (bitmap->width + Offset + Align - 1) & ~(Align - 1);
    unsigned h = (bitmap->rows + Offset + Align - 1) & ~(Align - 1);
    if (Width < pen.x + w) {
        pen.x = 0;
        pen.y += ymax;
        ymax = 0;
    }
    if (pen.y / Height != (pen.y + h) / Height) {
        image = addImage();
        pen.x = 0;
        pen.y = (pen.y / Height + 1) * Height;
        ymax = 0;
    }

    glyph->x = pen.x;
    glyph->y = pen.y;
    glyph->left = slot->metrics.horiBearingX;
    glyph->top = slot->metrics.horiBearingY;
    glyph->width = bitmap->width;
    glyph->height = bitmap->rows;
    glyph->advance = slot->advance.x;

    for (FT_Int i = glyph->y % Height, p = 0; p < bitmap->rows; ++i, ++p) {
        for (FT_Int j = glyph->x, q = 0; q < bitmap->width; ++j, ++q)
            image[i * Width + j] |= bitmap->buffer[p * bitmap->width + q];
    }
    assert(((glyph->width + Offset + Align -1) & ~(Align - 1)) <= w);

    pen.x += w;
    if (ymax < h)
        ymax = h;
    return image;
}

void FontTexture::drawBitmap(FontGlyph* glyph, FT_GlyphSlot slot, int level)
{
    uint8_t* image = images.back();

    FT_Bitmap* bitmap = &slot->bitmap;
    unsigned x = glyph->x >> level;
    unsigned y = (glyph->y % Height) >> level;
    uint8_t* m = getMipmapImage(image, level);
    unsigned px = Width >> level;
    for (FT_Int i = y, p = 0; p < bitmap->rows; ++i, ++p) {
        for (FT_Int j = x, q = 0; q < bitmap->width; ++j, ++q)
            m[i * px + j] = bitmap->buffer[p * bitmap->width + q];
    }

    glyph->width = std::max(static_cast<unsigned short>(bitmap->width << level), glyph->width);
    glyph->height = std::max(static_cast<unsigned short>(bitmap->rows << level), glyph->height);
}

float FontTexture::measureText(const char16_t* text, float point)
{
    char32_t u;
    int width = 0;
    while ((text = utf16to32(text, &u)) && u) {
        FontGlyph* glyph = getGlyph(u);
        width += glyph->advance;
    }
    return width / 64.0f * point / this->point;
}

size_t FontTexture::fitText(const char16_t* text, size_t length, float point, float& leftover, unsigned ws, size_t* next, float* required)
{
    const float scale = point / this->point / 64.0f;
    char32_t u = 0;
    size_t posLast = 0;
    TextIterator ti;  // TODO: keep one ti
    ti.setText(text, length);
    if (next)
        *next = 0;
    char32_t c = 0;
    while (ti.next() && c != '\n') {
        size_t pos = *ti;
        float advance = 0.0f;
        FontGlyph* glyph;
        for (size_t i = posLast; i < pos; ++i) {
            text = utf16to32(text, &c);
            if (c != '\n') {
                u = c;
                glyph = getGlyph(u);
                advance += glyph->advance;
            }
        }
        advance *= scale;
        if (leftover < advance && ws != Pre && ws != Nowrap) {
            if (u == u' ' && isCollapsingSpace(ws) && posLast < pos) {
                float trimmed = advance - glyph->advance * scale;
                if (trimmed <= leftover) {
                    leftover -= trimmed;
                    posLast = pos - 1;
                    break;
                }
            }
            if (next)
                *next = pos;
            if (required)
                *required = advance;
            break;
        }
        leftover -= advance;
        posLast = pos;
    }
    return posLast;
}

// TODO: Support full (language-specific) case mapping at some point.
// cf. http://userguide.icu-project.org/transforms/casemappings
std::u16string FontTexture::
fitTextWithTransformation(const char16_t* text, size_t length, float point, unsigned transform,
                          float& leftover,
                          size_t* lenght, size_t* transformedLength,
                          unsigned ws, size_t* next, float* required)
{
    std::u16string transformed;
    const float scale = point / this->point / 64.0f;
    char32_t u = 0;
    size_t posLast = 0;
    size_t posLastTransformed = 0;
    TextIterator ti;  // TODO: keep one ti
    ti.setText(text, length);
    if (next)
        *next = 0;
    char32_t c;
    while (ti.next() && c != '\n') {
        size_t pos = *ti;
        float advance = 0.0f;
        FontGlyph* glyph;
        for (size_t i = posLast; i < pos; ++i) {
            text = utf16to32(text, &c);
            if (c != '\n') {
                u = c;
                switch (transform) {
                case 1:  // capitalize
                    if (i == posLast)
                        u = u_totitle(u);
                    break;
                case 2:  // uppercase
                    u = u_toupper(u);
                    break;
                case 3:  // lowercase
                    u = u_tolower(u);
                    break;
                default:  // none
                    break;
                }
                char16_t buffer[3];
                if (char16_t* p = utf32to16(u, buffer)) {
                    *p = 0;
                    transformed += buffer;
                }
                glyph = getGlyph(u);
                advance += glyph->advance;
            }
        }
        advance *= scale;
        if (leftover < advance && ws != Pre && ws != Nowrap) {
            if (u == u' ' && isCollapsingSpace(ws) && posLast < pos) {
                float trimmed = advance - glyph->advance * scale;
                if (trimmed <= leftover) {
                    leftover -= trimmed;
                    posLast = pos - 1;
                    posLastTransformed = transformed.length() - 1;
                    transformed.erase(posLastTransformed);
                    break;
                }
            }
            if (next)
                *next = pos;
            if (required)
                *required = advance;
            break;
        }
        leftover -= advance;
        posLast = pos;
        posLastTransformed = transformed.length();
    }
    if (lenght)
        *lenght = posLast;
    if (transformedLength)
        *transformedLength = posLastTransformed;
    return transformed;
}<commit_msg>(FontTexture::FontTexture) : Fix bearingGap calculation when hinting is not used.<commit_after>/*
 * Copyright 2010, 2011 Esrille Inc.
 *
 * 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 "FontManager.h"

#include <algorithm>

#include <freetype/tttables.h>
#include <freetype/ftsizes.h>
#include <unicode/utypes.h>

#include "utf.h"
#include "TextIterator.h"

using namespace org::w3c::dom::bootstrap;

namespace {

// cf. CSSWhiteSpaceValueImp
enum {
    Normal,
    Pre,
    Nowrap,
    PreWrap,
    PreLine
};

bool isCollapsingSpace(unsigned value) {
    switch (value) {
    case Normal:
    case Nowrap:
    case PreLine:
        return true;
    default:
        return false;
    }
}

const bool USE_HINTING = false;

}
//
// FontManager
//

FontManager::~FontManager()
{
    for (std::map<std::string, FontFace*>::iterator it = faces.begin(); it != faces.end(); ++it)
        delete it->second;
    FT_Done_FreeType(library);
}

FontFace* FontManager::getFontFace(const std::string fontFilename) throw ()
{
    std::map<std::string, FontFace*>::iterator it = faces.find(fontFilename);
    if (it != faces.end())
        return it->second;
    FontFace* face = 0;
    try {
        face = new FontFace(this, fontFilename);
        faces.insert(std::pair<std::string, FontFace*>(fontFilename, face));
    } catch (...) {
        delete face;
        return 0;
    }
    return face;
}

//
// FontFace
//

FontFace::FontFace(FontManager* manager, const std::string fontFilename, long index) try :
    manager(manager),
    charmap(0)
{
    FT_Error error = FT_New_Face(manager->library, fontFilename.c_str(), index, &face);
    if (error) {
        face = 0;
        throw std::runtime_error(__func__);
    }
    initCharmap();
} catch (...) {
    if (face)
        FT_Done_Face(face);
    throw;
}

FontFace::~FontFace()
{
    for (std::map<unsigned int, FontTexture*>::iterator it = textures.begin(); it != textures.end(); ++it)
        delete it->second;
    FT_Done_Face(face);
}

FontTexture* FontFace::getFontTexture(unsigned int point)
{
    std::map<unsigned int, FontTexture*>::iterator it = textures.find(point);
    if (it != textures.end())
        return it->second;
    FontTexture* texture = 0;
    try {
        texture = new FontTexture(this, point);
        textures.insert(std::pair<unsigned int, FontTexture*>(point, texture));
    } catch (...) {
        delete texture;
        return 0;
    }
    return texture;
}

//
// FontTexture
//

FontTexture::FontTexture(FontFace* face, unsigned int point) try :
    face(face),
    glyphs(0),
    point(point),
    bearingGap(0.0f)
{
    addImage();
    glyphs = new FontGlyph[face->glyphCount];

    sizes[0] = face->face->size;
    for (int i = 1; i < Sizes; ++i) {
        FT_Error error = FT_New_Size(face->face, &sizes[i]);
        if (error)
            throw std::runtime_error(__func__);
    }

    unsigned px = (point * 96) / 72;  // TODO: make dpi configurable
    for (int i = 0; i < Sizes; ++i) {
        FT_Activate_Size(sizes[i]);
        FT_Error error = FT_Set_Pixel_Sizes(face->face, 0, px);
        if (error)
            throw std::runtime_error(__func__);
        px >>= 1;
    }
    FT_Activate_Size(sizes[0]);

    ascender = face->face->ascender;
    descender = face->face->descender;
    pen.x = pen.y = Offset;  // (0, 0) is reserved for an uninitialized font glyph
    ymax = 0;

    lineGap = 0;
    xHeight = ascender / 2;
    if (FT_IS_SFNT(face->face)) {
        if (TT_OS2_* os2 = static_cast<TT_OS2_*>(FT_Get_Sfnt_Table(face->face, ft_sfnt_os2))) {
            lineGap = os2->usWinAscent + os2->usWinDescent - face->face->units_per_EM;
            xHeight = os2->sxHeight;
            float b = (point * 96.0f) / 72.0f * 64.0f;
            b *= static_cast<float>(ascender) / (ascender - descender);
            if (USE_HINTING) {
                b /= 64.0f;
                bearingGap = (roundf(b) - b) * 64.0f;
            } else {
                bearingGap = ceilf(b) - b;
                if (0.0f < bearingGap)
                    bearingGap -= 64.0f;
            }
        }
    }

    // Store the missing glyph (0) as the 1st entry
    if (!storeGlyph(glyphs, 0))
        throw std::runtime_error(__func__);
} catch (...) {
    delete glyphs;
    throw;
}

FontTexture::~FontTexture()
{
    for (std::vector<uint8_t*>::iterator it = images.begin(); it != images.end(); ++it)
        deleteImage(*it);
    delete[] glyphs;
}

FontGlyph* FontTexture::getGlyph(int32_t ucode)
{
    std::vector<int32_t>::iterator result;
    result = std::lower_bound(face->charmap.begin(), face->charmap.end(), ucode);
    if (*result != ucode)
        return glyphs;
    FontGlyph* glyph = &glyphs[result - face->charmap.begin()];
    if (!glyph->isInitialized()) {
        FT_UInt glyphIndex = FT_Get_Char_Index(face->face, ucode);
        assert(glyphIndex);
        if (!glyphIndex)
            return glyphs;
        if (!storeGlyph(glyph, glyphIndex))
            return glyphs;
    }
    return glyph;
}

uint8_t* FontTexture::getImage(FontGlyph* glyph)
{
    // TODO: check range
    return images[glyph->y / Height];
}

bool FontTexture::storeGlyph(FontGlyph* glyph, FT_UInt glyphIndex)
{
    // load glyph image into the slot (erase previous one)
    FT_Error error = FT_Load_Glyph(face->face, glyphIndex, USE_HINTING ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING);
    if (error)
        return false;

    // convert to an anti-aliased bitmap
    error = FT_Render_Glyph(face->face->glyph, FT_RENDER_MODE_NORMAL);
    if (error)
        return false;

    uint8_t* image;

    FT_GlyphSlot slot = face->face->glyph;
    try {
        image = drawBitmap(glyph, slot);
    } catch (...) {
        return false;
    }
    assert(glyph->isInitialized());

    // update mipmap textures.
    for (int i = 1; i < Sizes; ++i) {
        FT_Activate_Size(sizes[i]);
        // load glyph image into the slot (erase previous one)
        FT_Error error = FT_Load_Glyph(face->face, glyphIndex, USE_HINTING ? FT_LOAD_DEFAULT : FT_LOAD_NO_HINTING);
        if (error)
            return false;
        // convert to an anti-aliased bitmap
        error = FT_Render_Glyph(face->face->glyph, FT_RENDER_MODE_NORMAL);
        if (error)
            return false;
        FT_GlyphSlot slot = face->face->glyph;
        try {
            drawBitmap(glyph, slot, i);
        } catch (...) {
            return false;
        }
    }
    FT_Activate_Size(sizes[0]);

    updateImage(image, glyph);

    return true;
}

uint8_t* FontTexture::drawBitmap(FontGlyph* glyph, FT_GlyphSlot slot)
{
    uint8_t* image = images.back();

    FT_Bitmap* bitmap = &slot->bitmap;
    unsigned w = (bitmap->width + Offset + Align - 1) & ~(Align - 1);
    unsigned h = (bitmap->rows + Offset + Align - 1) & ~(Align - 1);
    if (Width < pen.x + w) {
        pen.x = 0;
        pen.y += ymax;
        ymax = 0;
    }
    if (pen.y / Height != (pen.y + h) / Height) {
        image = addImage();
        pen.x = 0;
        pen.y = (pen.y / Height + 1) * Height;
        ymax = 0;
    }

    glyph->x = pen.x;
    glyph->y = pen.y;
    glyph->left = slot->metrics.horiBearingX;
    glyph->top = slot->metrics.horiBearingY;
    glyph->width = bitmap->width;
    glyph->height = bitmap->rows;
    glyph->advance = slot->advance.x;

    for (FT_Int i = glyph->y % Height, p = 0; p < bitmap->rows; ++i, ++p) {
        for (FT_Int j = glyph->x, q = 0; q < bitmap->width; ++j, ++q)
            image[i * Width + j] |= bitmap->buffer[p * bitmap->width + q];
    }
    assert(((glyph->width + Offset + Align -1) & ~(Align - 1)) <= w);

    pen.x += w;
    if (ymax < h)
        ymax = h;
    return image;
}

void FontTexture::drawBitmap(FontGlyph* glyph, FT_GlyphSlot slot, int level)
{
    uint8_t* image = images.back();

    FT_Bitmap* bitmap = &slot->bitmap;
    unsigned x = glyph->x >> level;
    unsigned y = (glyph->y % Height) >> level;
    uint8_t* m = getMipmapImage(image, level);
    unsigned px = Width >> level;
    for (FT_Int i = y, p = 0; p < bitmap->rows; ++i, ++p) {
        for (FT_Int j = x, q = 0; q < bitmap->width; ++j, ++q)
            m[i * px + j] = bitmap->buffer[p * bitmap->width + q];
    }

    glyph->width = std::max(static_cast<unsigned short>(bitmap->width << level), glyph->width);
    glyph->height = std::max(static_cast<unsigned short>(bitmap->rows << level), glyph->height);
}

float FontTexture::measureText(const char16_t* text, float point)
{
    char32_t u;
    int width = 0;
    while ((text = utf16to32(text, &u)) && u) {
        FontGlyph* glyph = getGlyph(u);
        width += glyph->advance;
    }
    return width / 64.0f * point / this->point;
}

size_t FontTexture::fitText(const char16_t* text, size_t length, float point, float& leftover, unsigned ws, size_t* next, float* required)
{
    const float scale = point / this->point / 64.0f;
    char32_t u = 0;
    size_t posLast = 0;
    TextIterator ti;  // TODO: keep one ti
    ti.setText(text, length);
    if (next)
        *next = 0;
    char32_t c = 0;
    while (ti.next() && c != '\n') {
        size_t pos = *ti;
        float advance = 0.0f;
        FontGlyph* glyph;
        for (size_t i = posLast; i < pos; ++i) {
            text = utf16to32(text, &c);
            if (c != '\n') {
                u = c;
                glyph = getGlyph(u);
                advance += glyph->advance;
            }
        }
        advance *= scale;
        if (leftover < advance && ws != Pre && ws != Nowrap) {
            if (u == u' ' && isCollapsingSpace(ws) && posLast < pos) {
                float trimmed = advance - glyph->advance * scale;
                if (trimmed <= leftover) {
                    leftover -= trimmed;
                    posLast = pos - 1;
                    break;
                }
            }
            if (next)
                *next = pos;
            if (required)
                *required = advance;
            break;
        }
        leftover -= advance;
        posLast = pos;
    }
    return posLast;
}

// TODO: Support full (language-specific) case mapping at some point.
// cf. http://userguide.icu-project.org/transforms/casemappings
std::u16string FontTexture::
fitTextWithTransformation(const char16_t* text, size_t length, float point, unsigned transform,
                          float& leftover,
                          size_t* lenght, size_t* transformedLength,
                          unsigned ws, size_t* next, float* required)
{
    std::u16string transformed;
    const float scale = point / this->point / 64.0f;
    char32_t u = 0;
    size_t posLast = 0;
    size_t posLastTransformed = 0;
    TextIterator ti;  // TODO: keep one ti
    ti.setText(text, length);
    if (next)
        *next = 0;
    char32_t c;
    while (ti.next() && c != '\n') {
        size_t pos = *ti;
        float advance = 0.0f;
        FontGlyph* glyph;
        for (size_t i = posLast; i < pos; ++i) {
            text = utf16to32(text, &c);
            if (c != '\n') {
                u = c;
                switch (transform) {
                case 1:  // capitalize
                    if (i == posLast)
                        u = u_totitle(u);
                    break;
                case 2:  // uppercase
                    u = u_toupper(u);
                    break;
                case 3:  // lowercase
                    u = u_tolower(u);
                    break;
                default:  // none
                    break;
                }
                char16_t buffer[3];
                if (char16_t* p = utf32to16(u, buffer)) {
                    *p = 0;
                    transformed += buffer;
                }
                glyph = getGlyph(u);
                advance += glyph->advance;
            }
        }
        advance *= scale;
        if (leftover < advance && ws != Pre && ws != Nowrap) {
            if (u == u' ' && isCollapsingSpace(ws) && posLast < pos) {
                float trimmed = advance - glyph->advance * scale;
                if (trimmed <= leftover) {
                    leftover -= trimmed;
                    posLast = pos - 1;
                    posLastTransformed = transformed.length() - 1;
                    transformed.erase(posLastTransformed);
                    break;
                }
            }
            if (next)
                *next = pos;
            if (required)
                *required = advance;
            break;
        }
        leftover -= advance;
        posLast = pos;
        posLastTransformed = transformed.length();
    }
    if (lenght)
        *lenght = posLast;
    if (transformedLength)
        *transformedLength = posLastTransformed;
    return transformed;
}<|endoftext|>


<commit_before>#include "xchainer/testing/util.h"

#include <atomic>
#include <cstdlib>
#include <string>

#include <gtest/gtest.h>

#include "xchainer/backend.h"
#include "xchainer/context.h"
#include "xchainer/error.h"

namespace xchainer {
namespace testing {

namespace {

std::atomic<int> g_skipped_native_test_count{0};
std::atomic<int> g_skipped_cuda_test_count{0};

int GetNativeDeviceLimit(Backend& backend) {
    assert(backend.GetName() == "native");
    static int limit = -1;
    if (limit >= 0) {
        return limit;
    }
    const char* env = std::getenv("XCHAINER_TEST_NATIVE_DEVICE_LIMIT");
    if (env == nullptr) {
        limit = backend.GetDeviceCount();
    } else {
        limit = std::stoi(env);
        if (limit < 0) {
            throw XchainerError("XCHAINER_TEST_NATIVE_DEVICE_LIMIT must be non-negative integer: " + std::string(env));
        }
    }
    return limit;
}

int GetCudaDeviceLimit(Backend& backend) {
    assert(backend.GetName() == "cuda");
    static int limit = -1;
    if (limit >= 0) {
        return limit;
    }
    const char* env = std::getenv("XCHAINER_TEST_CUDA_DEVICE_LIMIT");
    if (env == nullptr) {
        limit = backend.GetDeviceCount();
    } else {
        limit = std::stoi(env);
        if (limit < 0) {
            throw XchainerError("XCHAINER_TEST_CUDA_DEVICE_LIMIT must be non-negative integer: " + std::string(env));
        }
    }
    return limit;
}

}  // namespace

namespace internal {

int GetSkippedNativeTestCount() { return g_skipped_native_test_count; }

int GetSkippedCudaTestCount() { return g_skipped_cuda_test_count; }

int GetDeviceLimit(Backend& backend) {
    if (backend.GetName() == "native") {
        return GetNativeDeviceLimit(backend);
    }
    if (backend.GetName() == "cuda") {
        return GetCudaDeviceLimit(backend);
    }
    throw BackendError("invalid backend: " + backend.GetName());
}

bool SkipIfDeviceUnavailable(Backend& backend, int required_num) {
    if (GetDeviceLimit(backend) >= required_num) {
        return false;
    }
    const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
    std::cout << "[     SKIP ] " << test_info->test_case_name() << "." << test_info->name() << std::endl;

    if (backend.GetName() == "native") {
        ++g_skipped_native_test_count;
    } else if (backend.GetName() == "cuda") {
        ++g_skipped_cuda_test_count;
    } else {
        throw BackendError("invalid backend: " + backend.GetName());
    }
    return true;
}

bool SkipIfDeviceUnavailable(const std::string& backend_name, int required_num) {
    return SkipIfDeviceUnavailable(GetDefaultContext().GetBackend(backend_name), required_num);
}

}  // namespace internal
}  // namespace testing
}  // namespace xchainer
<commit_msg>Add missing include<commit_after>#include "xchainer/testing/util.h"

#include <atomic>
#include <cassert>
#include <cstdlib>
#include <string>

#include <gtest/gtest.h>

#include "xchainer/backend.h"
#include "xchainer/context.h"
#include "xchainer/error.h"

namespace xchainer {
namespace testing {

namespace {

std::atomic<int> g_skipped_native_test_count{0};
std::atomic<int> g_skipped_cuda_test_count{0};

int GetNativeDeviceLimit(Backend& backend) {
    assert(backend.GetName() == "native");
    static int limit = -1;
    if (limit >= 0) {
        return limit;
    }
    const char* env = std::getenv("XCHAINER_TEST_NATIVE_DEVICE_LIMIT");
    if (env == nullptr) {
        limit = backend.GetDeviceCount();
    } else {
        limit = std::stoi(env);
        if (limit < 0) {
            throw XchainerError("XCHAINER_TEST_NATIVE_DEVICE_LIMIT must be non-negative integer: " + std::string(env));
        }
    }
    return limit;
}

int GetCudaDeviceLimit(Backend& backend) {
    assert(backend.GetName() == "cuda");
    static int limit = -1;
    if (limit >= 0) {
        return limit;
    }
    const char* env = std::getenv("XCHAINER_TEST_CUDA_DEVICE_LIMIT");
    if (env == nullptr) {
        limit = backend.GetDeviceCount();
    } else {
        limit = std::stoi(env);
        if (limit < 0) {
            throw XchainerError("XCHAINER_TEST_CUDA_DEVICE_LIMIT must be non-negative integer: " + std::string(env));
        }
    }
    return limit;
}

}  // namespace

namespace internal {

int GetSkippedNativeTestCount() { return g_skipped_native_test_count; }

int GetSkippedCudaTestCount() { return g_skipped_cuda_test_count; }

int GetDeviceLimit(Backend& backend) {
    if (backend.GetName() == "native") {
        return GetNativeDeviceLimit(backend);
    }
    if (backend.GetName() == "cuda") {
        return GetCudaDeviceLimit(backend);
    }
    throw BackendError("invalid backend: " + backend.GetName());
}

bool SkipIfDeviceUnavailable(Backend& backend, int required_num) {
    if (GetDeviceLimit(backend) >= required_num) {
        return false;
    }
    const ::testing::TestInfo* const test_info = ::testing::UnitTest::GetInstance()->current_test_info();
    std::cout << "[     SKIP ] " << test_info->test_case_name() << "." << test_info->name() << std::endl;

    if (backend.GetName() == "native") {
        ++g_skipped_native_test_count;
    } else if (backend.GetName() == "cuda") {
        ++g_skipped_cuda_test_count;
    } else {
        throw BackendError("invalid backend: " + backend.GetName());
    }
    return true;
}

bool SkipIfDeviceUnavailable(const std::string& backend_name, int required_num) {
    return SkipIfDeviceUnavailable(GetDefaultContext().GetBackend(backend_name), required_num);
}

}  // namespace internal
}  // namespace testing
}  // namespace xchainer
<|endoftext|>