

//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
//_/_/
//_/_/  This file is part of CMSDK version 2.1.1.260726 (26 July 2026)
//_/_/  
//_/_/  developed by
//_/_/  
//_/_/  Communicative Machines Ltd. 
//_/_/    http://cmlabs.com/
//_/_/  Copyright (c) 2026
//_/_/  
//_/_/  in collaboration with 
//_/_/  
//_/_/  Icelandic Institute for Intelligent Machines ses
//_/_/    Menntavegur 1
//_/_/    101 Reykjavik
//_/_/    ICELAND
//_/_/    http://www.iiim.is/
//_/_/
//_/_/  and is released under the following
//_/_/  
//_/_/  EXTENDED BSD License
//_/_/
//_/_/  Redistribution and use in source and binary forms, with or without
//_/_/  modification, is permitted provided that the following conditions 
//_/_/  are met:
//_/_/
//_/_/  - Redistributions of source code must retain the above copyright 
//_/_/    and collaboration notice, this list of conditions and the following 
//_/_/    disclaimer.
//_/_/
//_/_/  - Redistributions in binary form must reproduce the above copyright 
//_/_/    notice, this list of conditions and the following disclaimer in the 
//_/_/    documentation and/or other materials provided with the distribution.
//_/_/
//_/_/  - Neither the name of its copyright holders nor the names of its 
//_/_/    contributors may be used to endorse or promote products derived from 
//_/_/    this software without specific prior written permission.
//_/_/
//_/_/  - CADIA Clause: The license granted in and to the software under this 
//_/_/    agreement is a limited-use license. The software may not be used in 
//_/_/    furtherance of: (i) intentionally causing bodily injury or severe 
//_/_/    emotional harm to any person; (ii) invading the personal privacy or 
//_/_/    violating the human rights of any person; or (iii) committing or 
//_/_/    preparing for any act of war. 
//_/_/
//_/_/  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
//_/_/  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
//_/_/  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
//_/_/  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 
//_/_/  OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
//_/_/  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
//_/_/  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
//_/_/  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
//_/_/  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
//_/_/  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
//_/_/  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//_/_/
//_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
/*! @file Utils.cpp
    @brief Implementation of the cross-platform utility layer declared in Utils.h.

    Contains the Windows/Linux/macOS implementations of synchronization
    primitives, shared memory, threads/processes, sockets, file and string
    helpers. See Utils.h for the API documentation. */
#ifdef _WIN32
	// To avoid complaints about fopen and _open
	#define _CRT_SECURE_NO_WARNINGS
#endif

#include "Utils.h"
#include "ObjectIDs.h"
#include "HTML.h"
#include "ThreadManager.h"
#include "UnitTestFramework.h"

#ifdef __APPLE__
#include <mach/vm_statistics.h>
#include <mach/host_info.h>
#include <mach/task_info.h>
#include <mach/thread_act.h>
#include <sys/sysctl.h>
#endif

namespace cmlabs {

//////////////////////////////////////////////////////////////
//                        INIT/EXIT                         //
//////////////////////////////////////////////////////////////

uint32 CleanShutdownInstanceCount = 0;
CleanShutdown CleanShutdownInstance;

CleanShutdown::CleanShutdown() {
	CleanShutdownInstanceCount++;
}

CleanShutdown::~CleanShutdown() {
	CleanShutdownInstanceCount--;
	if (!CleanShutdownInstanceCount) {
		ClearUtilsMaps();
		delete(LogSystem::LogSingleton);
		LogSystem::LogSingleton = NULL;
	}
	ThreadManager::Shutdown();
}

bool ClearUtilsMaps() {

	if (utils::SharedMemorySemaphoreMapMutex) {
		utils::SharedMemorySemaphoreMapMutex->enter();
		std::map<std::string, utils::Semaphore*>::iterator si;
		while (utils::SharedMemorySemaphoreMap->size()) {
			si = utils::SharedMemorySemaphoreMap->begin();
			delete(si->second);
			utils::SharedMemorySemaphoreMap->erase(si->first);
		}
		delete(utils::SharedMemorySemaphoreMap);
		utils::SharedMemorySemaphoreMap = NULL;
		utils::SharedMemorySemaphoreMapMutex->leave();
		delete(utils::SharedMemorySemaphoreMapMutex);
		utils::SharedMemorySemaphoreMapMutex = NULL;
	}
	
	if (utils::SharedMemoryEventMapMutex) {
		utils::SharedMemoryEventMapMutex->enter();
		std::map<std::string, utils::Event*>::iterator ei;
		while (utils::SharedMemoryEventMap->size()) {
			ei = utils::SharedMemoryEventMap->begin();
			delete(ei->second);
			utils::SharedMemoryEventMap->erase(ei->first);
		}
		delete(utils::SharedMemoryEventMap);
		utils::SharedMemoryEventMap = NULL;
		utils::SharedMemoryEventMapMutex->leave();
		delete(utils::SharedMemoryEventMapMutex);
		utils::SharedMemoryEventMapMutex = NULL;
	}

	if (utils::SharedMemoryMutexMapMutex) {
		utils::SharedMemoryMutexMapMutex->enter();
		std::map<std::string, utils::Mutex*>::iterator mi;
		while (utils::SharedMemoryMutexMap->size()) {
			mi = utils::SharedMemoryMutexMap->begin();
			delete(mi->second);
			utils::SharedMemoryMutexMap->erase(mi->first);
		}
		delete(utils::SharedMemoryMutexMap);
		utils::SharedMemoryMutexMap = NULL;
		utils::SharedMemoryMutexMapMutex->leave();
		delete(utils::SharedMemoryMutexMapMutex);
		utils::SharedMemoryMutexMapMutex = NULL;
	}

	delete(utils::DrumBeatMap);
	utils::DrumBeatMap = NULL;

	delete(utils::SharedMemoryFileHandleMap);
	utils::SharedMemoryFileHandleMap = NULL;

	delete(utils::Timer::timers);
	utils::Timer::timers = NULL;

	delete(utils::CommandLineInfo::CommandLineInfoSingleton);
	utils::CommandLineInfo::CommandLineInfoSingleton = NULL;

	return true;
}

utils::CommandLineInfo* utils::CommandLineInfo::CommandLineInfoSingleton = NULL;

//////////////////////////////////////////////////////////////
//                         Objects                          //
//////////////////////////////////////////////////////////////

std::string GetDataTypeName(uint32 datatype) {
	switch (datatype) {
	case CONSTCHARID:
		return "String";
	case TIMEID:
		return "Time";
	case INTID:
		return "Integer";
	case DOUBLEID:
		return "Float";
	case CHARDATAID:
		return "Binary";
	case DATAMESSAGEID:
		return "Message";
	case CONSTCHARINFOID:
		return "String info";
	case CHARDATAINFOID:
		return "Binary info";
	case DATAMESSAGEINFOID:
		return "Message info";
	default:
		return "Unknown type";
	}
}

uint32 GetDataTypeID(const char* typeName) {
	if ((stricmp(typeName, "string") == 0) || (stricmp(typeName, "text") == 0))
		return CONSTCHARID;
	else if (stricmp(typeName, "time") == 0)
		return CONSTCHARID;
	else if ((stricmp(typeName, "integer") == 0) || (stricmp(typeName, "int") == 0))
		return INTID;
	else if ((stricmp(typeName, "float") == 0) || (stricmp(typeName, "double") == 0))
		return DOUBLEID;
	else if (stricmp(typeName, "binary") == 0)
		return CHARDATAID;
	else if (stricmp(typeName, "message") == 0)
		return DATAMESSAGEID;
	else if (stricmp(typeName, "string info") == 0)
		return CONSTCHARINFOID;
	else if (stricmp(typeName, "binary info") == 0)
		return CHARDATAINFOID;
	else if (stricmp(typeName, "message info") == 0)
		return DATAMESSAGEINFOID;
	else
		return 0;
}

//////////////////////////////////////////////////////////////
//                         Logging                          //
//////////////////////////////////////////////////////////////

bool LogSystem::SetLogReceiver(LogReceiver* rec) {
	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}
	LogSingleton->logReceiverObj = rec;
	return true;
}

bool LogSystem::SetLogLevelDebug(uint8 level) {
	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}
	memset(LogSingleton->logLevelDebug, level, LOG_MAXCOUNT);
	return true;
}

bool LogSystem::SetLogLevelDebug(uint8 subject, uint8 level) {
	if (subject >= LOG_MAXCOUNT)
		return false;
	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}
	LogSingleton->logLevelDebug[subject] = level;
	return true;
}

bool LogSystem::SetLogLevelVerbose(uint8 level) {
	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}
	memset(LogSingleton->logLevelVerbose, level, LOG_MAXCOUNT);
	return true;
}

bool LogSystem::SetLogLevelVerbose(uint8 subject, uint8 level) {
	if (subject >= LOG_MAXCOUNT)
		return false;
	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}
	LogSingleton->logLevelVerbose[subject] = level;
	return true;
}


LogSystem::LogSystem() {
	logfile = NULL;
	printToStdOut = true;
}
LogSystem::~LogSystem() {
	if (logfile)
		delete [] logfile;
	logfile = NULL;
	printToStdOut = true;
}

const char* LogEntry::getText(uint32& len) {
	if (len = this->size - sizeof(LogEntry))
		return (char*)this+sizeof(LogEntry);
	else
		return NULL;
}

bool LogEntry::setText(char* text, uint32 len) {
	if (!text || !len)
		return false;
	int32 avail = (int32)size - sizeof(LogEntry) - 1;
	if (avail < (int32)len)
		return false;
	memcpy((char*)this+sizeof(LogEntry), text, len);
	*((char*)this+sizeof(LogEntry)+len) = 0;
	return true;
}

std::string LogEntry::toJSON() {
	uint32 len;
	const char* text = getText(len);
	return utils::StringFormat(
		"{ \"time\": %llu, \"level\": %u, \"source\": %u, \"subject\": %u, \"type\": %u, \"text\": \"%s\" }",
		time, level, source, subject, type, utils::EncodeJSON(text).c_str());
}

std::string LogEntry::toXML() {
	uint32 len;
	const char* text = getText(len);
	return utils::StringFormat(
		"<entry time=\"%llu\" level=\"%u\" source=\"%u\" subject=\"%u\" type=\"%u\" text=\"%s\" />\n",
		time, level, source, subject, type, html::EncodeHTML(text).c_str());
}


bool LogSystem::SetLogFileOutput(const char* logfile, bool printOut) {

	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}

	uint32 len;
	if (!logfile || !(len = (uint32)strlen(logfile))) {
		if (LogSingleton->logfile) {
			delete [] LogSingleton->logfile;
			LogSingleton->logfile = NULL;
		}
	}
	else {
		if (LogSingleton->logfile)
			delete [] LogSingleton->logfile;
		LogSingleton->logfile = new char[len+1];
		utils::strcpyavail(LogSingleton->logfile, logfile, len+1, true);
	}
	LogSingleton->printToStdOut = printOut;
	return true;
}

//#pragma data_seg(".shared")  // Begin the shared data segment.
LogSystem* LogSystem::LogSingleton = NULL;
//#pragma data_seg()          // End the shared data segment
//#pragma comment(linker, "/section:.shared,RWS")

bool LogSystem::LogSystemPrint(uint32 source, uint8 subject, uint8 level, const char *formatstring, ... ) {

	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}

	LogEntry* entry;
	char* str;
	uint32 len;
	bool res = true;

	if (LogSingleton->logLevelVerbose[subject] >= level) {
		va_list args;
		va_start(args, formatstring);
		str = utils::StringFormatVA(len, formatstring, args);
		va_end(args);
		if (!str || !len)
			res = false;
		else {
			if (LogSingleton->logReceiverObj) {
				entry = (LogEntry*) malloc(sizeof(LogEntry)+len+1);
				entry->size = sizeof(LogEntry)+len+1;
				entry->cid = LOGENTRYID;
				entry->time = GetTimeNow();
				entry->source = source;
				entry->subject = subject;
				entry->level = level;
				entry->type = LOGPRINT;
				entry->setText(str, len);
				free(str);
				return LogSingleton->logReceiverObj->logEntry(entry);
			}
			if (LogSingleton->logfile || LogSingleton->printToStdOut) {
				std::string strline = utils::StringFormat("%s %s\n", PrintTimeNowString().c_str(), str);
				if (LogSingleton->logfile && strlen(LogSingleton->logfile))
					utils::AppendToAFile(LogSingleton->logfile, strline.c_str(), (uint32)strline.length());
				if (LogSingleton->printToStdOut)
					std::cout << strline;
			}
		}
		free(str);
	}
	return res;
}

bool LogSystem::LogSystemDebug(uint32 source, uint8 subject, uint8 level, const char *formatstring, ... ) {

	if (!LogSingleton) {
		LogSingleton = new LogSystem();
		memset(LogSingleton->logLevelVerbose, 1, LOG_MAXCOUNT);
		memset(LogSingleton->logLevelDebug, 0, LOG_MAXCOUNT);
		LogSingleton->logReceiverObj = NULL;
	}

	LogEntry* entry;
	char* str;
	uint32 len;
	bool res = true;

	if (LogSingleton->logLevelDebug[subject] >= level) {
		va_list args;
		va_start(args, formatstring);
		str = utils::StringFormatVA(len, formatstring, args);
		va_end(args);
		if (!str || !len)
			res = false;
		else {
			if (LogSingleton->logReceiverObj) {
				entry = (LogEntry*) malloc(sizeof(LogEntry)+len+1);
				entry->size = sizeof(LogEntry)+len+1;
				entry->cid = LOGENTRYID;
				entry->time = GetTimeNow();
				entry->source = source;
				entry->subject = subject;
				entry->level = level;
				entry->type = LOGDEBUG;
				entry->setText(str, len);
				free(str);
				return LogSingleton->logReceiverObj->logEntry(entry);
			}
			else
				std::cout << PrintTimeNowString() << " [DEBUG] " << str << std::endl;
		}
		free(str);
	}
	return res;
}

#ifdef __APPLE__
	/*
	* A pthread_mutex_timedlock() impl for OSX/macOS, which lacks the
	* real thing.
	* NOTE: Unlike the real McCoy, won't return EOWNERDEAD, EDEADLK
	* or EOWNERDEAD
	*/
	static int macos_pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abs_timeout)
	{
		// abs_timeout is an ABSOLUTE deadline (CalcTimeout builds it from
		// gettimeofday()). The previous implementation mistook it for a relative
		// "remaining" duration and initialised its countdown to the absolute
		// epoch time (~1.78e9 s), so the deadline check effectively never fired
		// and mutex.enter(timeoutMS) would wait FOREVER whenever the lock was
		// actually held by another thread - a process-wide latent hang on macOS.
		// Compare the real clock against the deadline and poll trylock instead.
		int rv;
		while ((rv = pthread_mutex_trylock(mutex)) == EBUSY) {
			struct timeval now;
			gettimeofday(&now, NULL);
			if (now.tv_sec > abs_timeout->tv_sec ||
			    (now.tv_sec == abs_timeout->tv_sec &&
			     (long)(now.tv_usec) * 1000L >= abs_timeout->tv_nsec))
				return ETIMEDOUT;
			struct timespec nap = { 0, 1000000 };   // poll every 1ms
			nanosleep(&nap, NULL);
		}
		return rv;
	}
#endif


namespace utils {

#ifndef WINDOWS
//#if defined LINUX
	bool CalcTimeout(struct timespec &timeout, uint32 ms) {
		struct timeval now;
		if (gettimeofday(&now, NULL) != 0)
			return false;

		timeout.tv_sec = now.tv_sec + (ms / 1000);
		int64 us = (int64)(now.tv_usec) + ((ms % 1000)*1000);
		while (us >= 1000000) {
			timeout.tv_sec++;
			us -= 1000000;
		}
		timeout.tv_nsec = (long)(us * 1000); // usec -> nsec
		return true;
	}

	uint64 GetTime() {
		struct timeval tv; 
		if ( gettimeofday(&tv, NULL))
			return 0; 
		return (tv.tv_usec + tv.tv_sec * 1000000LL);
	}
#endif

// https://stackoverflow.com/questions/44807302/create-c-timer-in-macos/52905687#52905687

#ifdef WINDOWS
	VOID CALLBACK DrumBeatCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) {
		DrumBeatInfo* info = (DrumBeatInfo*) lpParameter;
		DrumBeatFunc func = info->func;
		if (!info->id || !info->createdTime || !info->started || !func)
			return;
		info->count++;
		if (!info->func(info->id, info->count))
			StopDrumBeat(info->id);
	}
#else
	#ifndef __APPLE__
	static void DrumBeatCallback(union sigval p) {
		DrumBeatInfo* info = (DrumBeatInfo*) p.sival_ptr;
		DrumBeatFunc func = info->func;
		if (!info->id || !info->createdTime || !info->started || !func)
			return;
		info->count++;
		if (!info->func(info->id, info->count))
			StopDrumBeat(info->id);
	}
	#endif
#endif

bool CreateDrumBeat(uint32 id, uint32 interval, DrumBeatFunc func, bool autostart) {

	if (!DrumBeatMap)
		DrumBeatMap = new std::map<uint32, DrumBeatInfo>;
	DrumBeatInfo* info = &(*DrumBeatMap)[id];

	// check if it already exists
	if (info->id && info->createdTime)
		return false;

	// Windows threadpool size limit is 500, we probably shouldn't create more than that anyway
	// Could change the limit with WT_SET_MAX_THREADPOOL_THREAD
	if (DrumBeatMap->size() > 450)
		return false;

	info->id = id;
	info->createdTime = GetTimeNow();
	info->func = func;
	info->count = 0;
	info->interval = interval;

	#ifdef WINDOWS
	#else
		#ifdef __APPLE__
			dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
			dispatch_source_t timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
			if (timer_source)
				info->handle = timer_source;
		#else
			// struct sigaction sa;
			struct sigevent timer_event;

			//sigemptyset(&sa.sa_mask);
			//sa.sa_flags = SA_SIGINFO;   /* Real-Time signal */
			//sa.sa_sigaction = timer_signal_handler;
			//sigaction(SIGRTMIN, &sa, NULL);

			timer_event.sigev_notify = SIGEV_THREAD;
			timer_event.sigev_notify_attributes = NULL;
			timer_event.sigev_notify_function = DrumBeatCallback;
			timer_event.sigev_value.sival_ptr = (void *)info;
			int ret = timer_create(CLOCK_REALTIME, &timer_event, &info->handle);
			if (ret != 0) {
				LogPrint(0,LOG_SYSTEM,0,"Error creating timer: %d", ret);
			}
		#endif
	#endif

	if (autostart)
		StartDrumBeat(id);
	return true;
}

bool StartDrumBeat(uint32 id) {
	if (!DrumBeatMap)
		return false;
	DrumBeatInfo* info = &(*DrumBeatMap)[id];
	if (!info->id || !info->createdTime)
		return false;

	if (info->started)
		return true;

	#ifdef WINDOWS
		// To compile an application that uses this function, define _WIN32_WINNT as 0x0500 or later
		CreateTimerQueueTimer(&info->handle, NULL, DrumBeatCallback, info, info->interval, info->interval, WT_EXECUTEINTIMERTHREAD);
		info->started = info->createdTime;
	#else
		#ifdef __APPLE__
			if (info->handle) {
				uint64 intervalNs = (uint64)info->interval * 1000000;
				dispatch_source_set_timer(info->handle, dispatch_time(DISPATCH_TIME_NOW, (int64_t)intervalNs), intervalNs, 0);
				DrumBeatInfo* inf = info;
				dispatch_source_set_event_handler(info->handle, ^{
					inf->count++;
					if (!inf->func(inf->id, inf->count))
						StopDrumBeat(inf->id);
				});
				dispatch_resume(info->handle);
				info->started = info->createdTime;
			}
		#else
			struct itimerspec newtv;
			uint64 p = info->interval * 1000; // convert to nanosec
			newtv.it_interval.tv_sec = p / 1000000; 
			newtv.it_interval.tv_nsec = (p % 1000000)*1000; 
			newtv.it_value.tv_sec = p / 1000000; 
			newtv.it_value.tv_nsec = (p % 1000000)*1000; 
			
			int ret = timer_settime(info->handle, 0, &newtv, NULL);
			if (ret != 0) {
				LogPrint(0,LOG_SYSTEM,0,"Error arming timer: %d", ret);
			}
			info->started = info->createdTime;
		#endif
	#endif
	return true;
}

bool StopDrumBeat(uint32 id) {
	if (!DrumBeatMap)
		return false;
	DrumBeatInfo* info = &(*DrumBeatMap)[id];
	if (!info->id || !info->createdTime)
		return false;

	if (!info->started)
		return true;

	#ifdef WINDOWS
		DeleteTimerQueueTimer(NULL, info->handle, NULL);
		info->started = 0;
		info->handle = NULL;
	#else
		#ifdef __APPLE__
			if (info->handle) {
				dispatch_suspend(info->handle);
				info->started = 0;
			}
		#else
			struct itimerspec newtv;
			memset(&newtv, 0, sizeof(itimerspec));
			int ret = timer_settime(info->handle, 0, &newtv, NULL);
			if (ret != 0) {
				LogPrint(0,LOG_SYSTEM,0,"Error arming timer: %d", ret);
			}
			info->started = 0;
		#endif
	#endif
	return true;
}

bool EndDrumBeat(uint32 id) {
	if (!DrumBeatMap)
		return false;
	DrumBeatInfo* info = &(*DrumBeatMap)[id];
	if (!info->id || !info->createdTime)
		return false;

	#ifdef WINDOWS
		if (info->started)
			StopDrumBeat(id);
	#else
		#ifdef __APPLE__
			if (info->handle) {
				if (info->started)
					dispatch_suspend(info->handle);
				dispatch_source_cancel(info->handle);
				dispatch_release(info->handle);
				info->handle = NULL;
				info->started = 0;
			}
		#else
			timer_delete(info->handle);
			info->started = 0;
			info->handle = 0;
		#endif
	#endif

	info->createdTime = 0;
	info->func = NULL;
	info->count = 0;
	info->interval = 0;
	return true;
}


bool EnterMutex(const char* name, uint32 ms, bool autocreate) {
	if (!SharedMemoryMutexMap)
		SharedMemoryMutexMap = new std::map<std::string, Mutex*>;
	if (!SharedMemoryMutexMapMutex)
		SharedMemoryMutexMapMutex = new Mutex();

	Mutex* mutex;
	std::map<std::string, Mutex*>::iterator i = SharedMemoryMutexMap->find(name);
	std::map<std::string, Mutex*>::iterator e = SharedMemoryMutexMap->end();
	if (i == e) {
		SharedMemoryMutexMapMutex->enter();
		i = SharedMemoryMutexMap->find(name);
		if (i == e) {
			if (!autocreate) {
				SharedMemoryMutexMapMutex->leave();
				return false;
			}
			mutex = new Mutex(name);
			SharedMemoryMutexMap->insert(SharedMemoryMutexMapPair(name, mutex));
		}
		else {
			// try again
			if (i->second)
				mutex = i->second;
			else {
				if (!autocreate) {
					SharedMemoryMutexMapMutex->leave();
					return false;
				}
				mutex = new Mutex(name);
				SharedMemoryMutexMap->insert(SharedMemoryMutexMapPair(name, mutex));
			}
		}
		SharedMemoryMutexMapMutex->leave();
	}
	else
		mutex = i->second;
	return (mutex->enter(ms));	
}

bool LeaveMutex(const char* name) {
	if (!SharedMemoryMutexMap)
		SharedMemoryMutexMap = new std::map<std::string, Mutex*>;
	if (!SharedMemoryMutexMapMutex)
		SharedMemoryMutexMapMutex = new Mutex();

	std::map<std::string, Mutex*>::iterator i = SharedMemoryMutexMap->find(name);
	std::map<std::string, Mutex*>::iterator e = SharedMemoryMutexMap->end();
	if (i == e)
		return false;
	return (i->second->leave());
}

bool DestroyMutex(const char* name) {
	if (!SharedMemoryMutexMap)
		SharedMemoryMutexMap = new std::map<std::string, Mutex*>;
	if (!SharedMemoryMutexMapMutex)
		SharedMemoryMutexMapMutex = new Mutex();
	SharedMemoryMutexMapMutex->enter();

	std::map<std::string, Mutex*>::iterator i = SharedMemoryMutexMap->find(name);
	std::map<std::string, Mutex*>::iterator e = SharedMemoryMutexMap->end();
	if (i == e)
		return false;
	delete(i->second);
	SharedMemoryMutexMap->erase(i);
	SharedMemoryMutexMapMutex->leave();
	return true;
}








Semaphore* GetSemaphore(const char* name, bool autocreate) {
	if (!SharedMemorySemaphoreMap)
		SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
	if (!SharedMemorySemaphoreMapMutex)
		SharedMemorySemaphoreMapMutex = new Mutex();
	Semaphore* semaphore;
	std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
	std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
	if (i == e) {
		SharedMemorySemaphoreMapMutex->enter();
		i = SharedMemorySemaphoreMap->find(name);
		if (i == e) {
			if (!autocreate) {
				SharedMemorySemaphoreMapMutex->leave();
				return NULL;
			}
			semaphore = new Semaphore(name);
			SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
		}
		else {
			// try again
			if (i->second)
				semaphore = i->second;
			else {
				if (!autocreate) {
					SharedMemorySemaphoreMapMutex->leave();
					return NULL;
				}
				semaphore = new Semaphore(name);
				SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
			}
		}
		SharedMemorySemaphoreMapMutex->leave();
	}
	else
		semaphore = i->second;
	return semaphore;
}

bool WaitForSemaphore(const char* name, uint32 ms, bool autocreate) {
	if (!SharedMemorySemaphoreMap)
		SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
	if (!SharedMemorySemaphoreMapMutex)
		SharedMemorySemaphoreMapMutex = new Mutex();
	Semaphore* semaphore;
	std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
	std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
	if (i == e) {
		SharedMemorySemaphoreMapMutex->enter();
		i = SharedMemorySemaphoreMap->find(name);
		if (i == e) {
			if (!autocreate) {
				SharedMemorySemaphoreMapMutex->leave();
				return false;
			}
			semaphore = new Semaphore(name);
			SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
		}
		else {
			// try again
			if (i->second)
				semaphore = i->second;
			else {
				if (!autocreate) {
					SharedMemorySemaphoreMapMutex->leave();
					return false;
				}
				semaphore = new Semaphore(name);
				SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
			}
		}
		SharedMemorySemaphoreMapMutex->leave();
	}
	else
		semaphore = i->second;
	return (semaphore->wait(ms));
}

bool SignalSemaphore(const char* name) {
	if (!SharedMemorySemaphoreMap)
		SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
	if (!SharedMemorySemaphoreMapMutex)
		SharedMemorySemaphoreMapMutex = new Mutex();

	Semaphore* semaphore;
	std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
	std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
	if (i == e) {
		SharedMemorySemaphoreMapMutex->enter();
		i = SharedMemorySemaphoreMap->find(name);
		if (i == e) {
			semaphore = new Semaphore(name);
			SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
		}
		else {
			// try again
			if (i->second)
				semaphore = i->second;
			else {
				semaphore = new Semaphore(name);
				SharedMemorySemaphoreMap->insert(SharedMemorySemaphoreMapPair(name, semaphore));
			}
		}
		SharedMemorySemaphoreMapMutex->leave();
	}
	else
		semaphore = i->second;
	return (semaphore->signal());	
}

bool DestroySemaphore(const char* name) {
	if (!SharedMemorySemaphoreMap)
		SharedMemorySemaphoreMap = new std::map<std::string, Semaphore*>;
	if (!SharedMemorySemaphoreMapMutex)
		SharedMemorySemaphoreMapMutex = new Mutex();
	SharedMemorySemaphoreMapMutex->enter();

	std::map<std::string, Semaphore*>::iterator i = SharedMemorySemaphoreMap->find(name);
	std::map<std::string, Semaphore*>::iterator e = SharedMemorySemaphoreMap->end();
	if (i == e) {
		SharedMemorySemaphoreMapMutex->leave();
		return false;
	}
	delete(i->second);
	SharedMemorySemaphoreMap->erase(i);
	SharedMemorySemaphoreMapMutex->leave();
	return true;
}











bool WaitForNextEvent(const char* name, uint32 ms, bool autocreate) {
	if (!SharedMemoryEventMap)
		SharedMemoryEventMap = new std::map<std::string, Event*>;
	if (!SharedMemoryEventMapMutex)
		SharedMemoryEventMapMutex = new Mutex();
	Event* event;
	std::map<std::string, Event*>::iterator i = SharedMemoryEventMap->find(name);
	std::map<std::string, Event*>::iterator e = SharedMemoryEventMap->end();
	if (i == e) {
		SharedMemoryEventMapMutex->enter();
		i = SharedMemoryEventMap->find(name);
		if (i == e) {
			if (!autocreate) {
				SharedMemoryEventMapMutex->leave();
				return false;
			}
			event = new Event(name);
			SharedMemoryEventMap->insert(SharedMemoryEventMapPair(name, event));
		}
		else {
			// try again
			if (i->second)
				event = i->second;
			else {
				if (!autocreate) {
					SharedMemoryEventMapMutex->leave();
					return false;
				}
				event = new Event(name);
				SharedMemoryEventMap->insert(SharedMemoryEventMapPair(name, event));
			}
		}
		SharedMemoryEventMapMutex->leave();
	}
	else
		event = i->second;
	return (event->waitNext(ms));	
}

bool SignalEvent(const char* name) {
	if (!SharedMemoryEventMap)
		SharedMemoryEventMap = new std::map<std::string, Event*>;
	if (!SharedMemoryEventMapMutex)
		SharedMemoryEventMapMutex = new Mutex();
	std::map<std::string, Event*>::iterator i = SharedMemoryEventMap->find(name);
	std::map<std::string, Event*>::iterator e = SharedMemoryEventMap->end();
	if (i == e)
		return false;
	else
		return (i->second->signal());	
}

bool DestroyEvent(const char* name) {
	if (!SharedMemoryEventMap)
		SharedMemoryEventMap = new std::map<std::string, Event*>;
	if (!SharedMemoryEventMapMutex)
		SharedMemoryEventMapMutex = new Mutex();
	SharedMemoryEventMapMutex->enter();

	std::map<std::string, Event*>::iterator i = SharedMemoryEventMap->find(name);
	std::map<std::string, Event*>::iterator e = SharedMemoryEventMap->end();
	if (i == e)
		return false;
	delete(i->second);
	SharedMemoryEventMap->erase(i);
	SharedMemoryEventMapMutex->leave();
	return true;
}





bool SetSharedSystemInstance(uint32 inst) {
	SharedSystemInstance = inst;
	return true;
}

uint32 GetSharedSystemInstance() {
	return SharedSystemInstance;
}


Mutex::Mutex() {
	//uint32 tt;
	//utils::GetCurrentThreadOSID(tt);
	//printf("Mutex[%p] create by thread: %u\n", this, tt);
	name = NULL;
	osid = 0;
	count = 0;
	total = 0;
	created = true;
	#ifdef WINDOWS
		mutex = ::CreateMutex(NULL, FALSE, NULL);
	#else
		mutex = new pthread_mutex_t;
		pthread_mutexattr_t attr;
		pthread_mutexattr_init(&attr);
		pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
		pthread_mutex_init(mutex, &attr);

		//semaphore = new sem_t;
		//if (sem_init(semaphore, 0, 1) != 0) {
		//	delete(semaphore);
		//	semaphore = NULL;
		//}

		pthread_mutexattr_destroy(&attr);
	#endif
}

Mutex::Mutex(const char* name, bool force) {
	this->name = new char[MAXSHMEMNAMELEN];
	osid = 0;
	count = 0;
	total = 0;
	created = true;
	#ifdef WINDOWS
		//sprintf(this->name, "Global\\Mutex_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(this->name, MAXSHMEMNAMELEN, "%s\\Mutex_%s", WINSHMEMSPACE, name);
		mutex = ::CreateMutex(NULL, FALSE, this->name);
	#else
		mutex = NULL;
//		sprintf(this->name, "/Cond_%s", name);
//		semaphore = sem_open(name, O_CREAT, 0666, 1);

		snprintf(this->name, MAXSHMEMNAMELEN, "Mutex_%s", name);
		
		// test if shared memory exists
		char* data = NULL;
		
		if (!force)
			data = OpenSharedMemorySegment(this->name, MUTEXMEMSIZE);
		if (data) {
			mutex = (pthread_mutex_t*) data;
			created = false;
		}
		else {
			// create shared mutex
			if (data = CreateSharedMemorySegment(this->name, MUTEXMEMSIZE, force)) {
				// create mutex
				mutex = (pthread_mutex_t*) data;
				pthread_mutexattr_t attr;
				pthread_mutexattr_init(&attr);
				pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
				pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
				pthread_mutex_init(mutex, &attr);
				pthread_mutexattr_destroy(&attr);
			}
		}
	#endif
}

#ifdef WINDOWS
bool CloseMutexHandle(HANDLE mutex) {
	// had to do this to catch exceptions in 32-bit Windows
	// as using __try+__except in C++ objects gave linker error
	__try {
		CloseHandle(mutex);
	}
	__except (EXCEPTION_EXECUTE_HANDLER) {
	}
	return true;
}
#endif

Mutex::~Mutex() {
	//uint32 tt;
	//utils::GetCurrentThreadOSID(tt);
	//printf("Mutex[%p] DELETE by thread: %u\n", this, tt);
	#ifdef WINDOWS
		if (mutex == NULL)
			return;

		CloseMutexHandle(mutex);
		mutex = NULL;
		delete [] name;
	#else
		if (!name) {
			pthread_mutex_destroy(mutex);
		//	sem_destroy(semaphore);
		//	delete(semaphore);
		}
		else {
			if (created)
				pthread_mutex_destroy(mutex);
			CloseSharedMemorySegment((char*)mutex, MUTEXMEMSIZE);
			mutex = NULL;
		//	sem_close(semaphore);
			delete [] name;
		}
	#endif
}

bool Mutex::enter() {
	#ifdef WINDOWS
		DWORD reply;
		reply = WaitForSingleObject(mutex, INFINITE);
		// WAIT_ABANDONED means the previous owner died while holding the mutex;
		// ownership IS granted to us, so treat it as a successful (if suspect) lock.
		// Returning false here while secretly owning the mutex would deadlock all
		// future waiters, which is exactly what intermittently broke the CMSDK tests.
		if (reply == WAIT_OBJECT_0 || reply == WAIT_ABANDONED) {
			utils::GetCurrentThreadOSID(osid);
//			if (count)
//				int n=0;
			count++;
			total++;
			return true;
		}
		else if (reply == WAIT_FAILED && GetLastError() == ERROR_INVALID_HANDLE) {
			// The mutex handle is invalid (closed elsewhere, e.g. during teardown
			// storms where a stale/double CloseHandle invalidated it). Mirror the
			// timeout variant: treat as acquired so callers (typically shutdown
			// paths) proceed instead of failing; hanging here forever was the
			// alternative when the handle value got recycled into another object.
			LogPrint(0, LOG_SYSTEM, 1, "Mutex::enter on invalid handle [%p] - treating as acquired", mutex);
			return true;
		}
		else {
			return false;
		}
	#else
		int error = pthread_mutex_lock(mutex);
		if (error == 0) {
			utils::GetCurrentThreadOSID(osid);
			count++;
			return true;
		}
		else {
			LogPrint(0, LOG_SYSTEM, 0, "Lock of mutex failed: %s (%d)", name ? name : "-", error);
			return false;
		}
	#endif
}

bool Mutex::enter(uint32 timeout, const char* errorMsg) {
//	printf("*** %s ***\n", errorMsg);
//	uint32 tt;
//	utils::GetCurrentThreadOSID(tt);
//	printf("Mutex[%p] locking by thread: %u\n", this, tt);

	#ifdef WINDOWS
		DWORD reply;
		reply = WaitForSingleObject(mutex, timeout);
		// See Mutex::enter(): WAIT_ABANDONED grants ownership; treat as success.
		if (reply == WAIT_OBJECT_0 || reply == WAIT_ABANDONED) {
			utils::GetCurrentThreadOSID(osid);
			//if (count)
			//	printf("*** [%u]Mutex[%p](%s) locked more than once: %u\n", osid, mutex, errorMsg, count);
			//else
			//	printf("--- [%u]Mutex[%p](%s) locked\n", osid, mutex, errorMsg);
		//	if (count)
		//		printf("Mutex[%p] double locking by thread: %u\n", this, tt);

			count++;
			total++;
			return true;
		}
		//if (errorMsg) {
			uint32 osid2;
			utils::GetCurrentThreadOSID(osid2);
			if (reply == WAIT_TIMEOUT) {
				LogPrint(0, LOG_SYSTEM, 0, "[%u]Mutex enter timeout[%p](%s) - currently held by %u, count %u, total %u",
					osid2, mutex, errorMsg ? errorMsg : "-", osid, count, total);
				//if (!errorMsg)
				//	int n = 0;
			}
			else if (reply == WAIT_FAILED) {
				DWORD mtxErr = GetLastError();
				if (mtxErr == ERROR_INVALID_HANDLE)
					return true;
				char* msg = new char[2048];
				int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
					NULL, mtxErr, 0, msg, 2048, NULL);
				msg[length] = '\0';
				LogPrint(0, LOG_SYSTEM, 0, "[%u]Mutex enter failed[%p](%s): %s - currently held by %u, count %u, total %u",
					osid2, mutex, errorMsg ? errorMsg : "-", msg, osid, count, total);
				delete[] msg;
			}
			else
				//[36136]Mutex enter error[00000000000016E0](-): 4294967295 - currently held by 0, count 0, total 7
				LogPrint(0,LOG_SYSTEM,0,"[%u]Mutex enter error[%p](%s): %d - currently held by %u, count %u, total %u",
				osid2, mutex, errorMsg ? errorMsg : "-", reply, osid, count, total);
		//}
		return false;
	#else
		struct timespec ts;
		CalcTimeout(ts, timeout);
		#ifdef __APPLE__
			int error = macos_pthread_mutex_timedlock(mutex, &ts);
		#else
			int error = pthread_mutex_timedlock(mutex, &ts);
		#endif
		if (error == 0) {
			utils::GetCurrentThreadOSID(osid);
			count++;
		//	if (count > 1)
		//		printf("Lock count now %u...\n", count);
		//	LogPrint(0,LOG_SYSTEM,0,"Mutex enter success: '%s' (%d) [%p] - I am %u", errorMsg, error, this, osid);
			return true;
		}
		else if (error == EINVAL) {
			LogPrint(0,LOG_SYSTEM,0,"Mutex enter error: '%s' (%d) [%u = %llu.%lld]",
				errorMsg ? errorMsg : "-", error, timeout, (uint64)(ts.tv_sec), (int64)(ts.tv_nsec));
			return false;
		}
//		if (errorMsg)
			LogPrint(0,LOG_SYSTEM,0,"Mutex enter timeout %u: '%s' (%d) [%p] - held by %u", timeout, errorMsg ? errorMsg : "-", error, this, osid);
		return false;
	
		//if (pthread_mutex_trylock(mutex) == 0)
		//	return true;

		//printf("[w-%s]", errorMsg);
		//fflush(stdout);
		//// Mutex is already locked, wait for the semaphore signal...
		//uint64 start = GetTimeNow();
		//struct timespec ts;
		//CalcTimeout(ts, timeout);

		//int res = 1;
		//do {
		//	if (sem_timedwait(semaphore, &ts) != 0)
		//		break;
		//	res = pthread_mutex_trylock(mutex);
		//} while ( !res && (GetTimeAgeMS(start) < (int32)timeout) );

		//if (res) {
		//	printf("s");
		//	fflush(stdout);
		//	return true;
		//}
		//if (errorMsg)
		//	LogPrint(0,LOG_SYSTEM,0,"Mutex enter timeout: %s", errorMsg);
		//return false;
	#endif
}

bool Mutex::leave() {
//	uint32 tt;
//	utils::GetCurrentThreadOSID(tt);
//	printf("Mutex[%p] leaving by thread: %u\n", this, tt);
	#ifdef WINDOWS
		uint32 osid2 = osid;
		//utils::GetCurrentThreadOSID(osid2);
		//else
		//	return true;
		//if (osid != osid2)
		//	printf("*** [%u]Mutex[%p] unlocked by someone else: %u\n", osid, mutex, osid2);
		//if (!count)
		//	osid = 0;
		if (ReleaseMutex(mutex) != 0) {
			if (count)
				count--;
			if (!count)
				osid = 0;
			//else
			//	printf("Mutex[%p] double release by thread: %u\n", this, tt);
			//osid = 0;
			return true;
		}
		else {
			// reinstate values
			//count++;
			osid = osid2;
			return false;
		}
	#else
		if (pthread_mutex_unlock(mutex) != 0)
			return false;
		else {
			osid = 0;
			count--;
			return true;
		}

		// Signal semaphore
		// return (sem_post(semaphore) == 0);
	#endif
}

//bool Mutex::destroy() {
//	#ifdef WINDOWS
//	#else
//		pthread_mutex_destroy(mutex);
//		DestroySharedMemorySegment((char*)mutex, MUTEXMEMSIZE);
//		mutex = NULL;
//		sem_unlink(name);
//	#endif
//	return true;
//}













Semaphore::Semaphore(uint32 maxCount) {
	name = NULL;
	#ifdef WINDOWS
		semaphore = CreateSemaphore( 
			NULL,			// no security attributes
			0,				// initial count
			10,				// maximum count
			NULL);			// unnamed semaphore
	#else
		#ifdef __APPLE__
			semaphore_impl = dispatch_semaphore_create(0);
		#else
			semaphore = new sem_t;
			if (sem_init(semaphore, 0, 0) != 0) {
				delete(semaphore);
				semaphore = NULL;
			}
		#endif
	#endif
}

Semaphore::Semaphore(const char* name, uint32 maxCount) {
	this->name = new char[MAXSHMEMNAMELEN];
	#ifdef WINDOWS
		//sprintf(this->name, "Global\\Semaphore_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(this->name, MAXSHMEMNAMELEN, "%s\\Semaphore_%s", WINSHMEMSPACE, name);
		// Creates or gets an existing Semaphore
		semaphore = CreateSemaphore( 
			NULL,			// no security attributes
			0,				// initial count
			maxCount,		// maximum count
			this->name);	// unnamed semaphore
	#else
		#ifdef __APPLE__
			char shmemname[MAXSHMEMNAMELEN];
			snprintf(shmemname, sizeof(shmemname), "Semaphore_%s", name);
			char* data = OpenSharedMemorySegment(shmemname, SEMAPHOREMEMSIZE);
			if (!data) {
				data = CreateSharedMemorySegment(shmemname, SEMAPHOREMEMSIZE, true);
				if (data) {
					pthread_mutex_t* m = (pthread_mutex_t*)data;
					pthread_mutexattr_t attr;
					pthread_mutexattr_init(&attr);
					pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
					pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
					pthread_mutex_init(m, &attr);
					pthread_mutexattr_destroy(&attr);
					pthread_cond_t* c = (pthread_cond_t*)(data + sizeof(pthread_mutex_t));
					pthread_condattr_t cattr;
					pthread_condattr_init(&cattr);
					pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
					pthread_cond_init(c, &cattr);
					pthread_condattr_destroy(&cattr);
					uint32* cnt = (uint32*)(data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t));
					*cnt = 1;
				}
			}
			semaphore_impl = data;
		#else
			snprintf(this->name, MAXSHMEMNAMELEN, "/Semaphore_%s", name);
//		sprintf(this->name, "/Semaphore_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
			semaphore = sem_open(this->name, O_CREAT, 0666, 1);
			if (semaphore == SEM_FAILED)
				semaphore = NULL;
		#endif
	#endif
	// Rewrite the original name
	utils::strcpyavail(this->name, name, MAXKEYNAMELEN, true);
}

Semaphore::~Semaphore() {
	#ifdef WINDOWS
		if (semaphore) {
			CloseHandle(semaphore);
			semaphore = NULL;
		}
		delete [] name;
	#else
		#ifdef __APPLE__
			if (!name) {
				if (semaphore_impl)
					dispatch_release((dispatch_semaphore_t)semaphore_impl);
				semaphore_impl = NULL;
			}
			else {
				if (semaphore_impl) {
					CloseSharedMemorySegment((char*)semaphore_impl, SEMAPHOREMEMSIZE);
					semaphore_impl = NULL;
				}
				delete [] name;
			}
		#else
			// printf("Deleting semaphore '%s'...\n", name);
			if (!name) {
				sem_destroy(semaphore);
				delete(semaphore);
			}
			else {
				sem_close(semaphore);
				delete [] name;
			}
		#endif
	#endif
}

bool Semaphore::wait() {
	#ifdef WINDOWS
		return (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0);
	#else
		#ifdef __APPLE__
			if (!name)
				return dispatch_semaphore_wait((dispatch_semaphore_t)semaphore_impl, DISPATCH_TIME_FOREVER) == 0;
			else {
				char* data = (char*)semaphore_impl;
				if (!data) return false;
				pthread_mutex_t* m = (pthread_mutex_t*)data;
				pthread_cond_t* c = (pthread_cond_t*)(data + sizeof(pthread_mutex_t));
				uint32* cnt = (uint32*)(data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t));
				pthread_mutex_lock(m);
				while (*cnt == 0)
					pthread_cond_wait(c, m);
				(*cnt)--;
				pthread_mutex_unlock(m);
				return true;
			}
		#else
			int r = sem_wait(semaphore);
			return r == 0;
		#endif
	#endif
}

bool Semaphore::wait(uint32 timeout) {
	#ifdef WINDOWS
		return (WaitForSingleObject(semaphore, timeout) == WAIT_OBJECT_0);
	#else
		#ifdef __APPLE__
			if (!name) {
				dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)timeout * 1000000);
				return dispatch_semaphore_wait((dispatch_semaphore_t)semaphore_impl, when) == 0;
			}
			else {
				char* data = (char*)semaphore_impl;
				if (!data) return false;
				pthread_mutex_t* m = (pthread_mutex_t*)data;
				pthread_cond_t* c = (pthread_cond_t*)(data + sizeof(pthread_mutex_t));
				uint32* cnt = (uint32*)(data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t));
				pthread_mutex_lock(m);
				struct timespec t;
				CalcTimeout(t, timeout);
				while (*cnt == 0) {
					int r = pthread_cond_timedwait(c, m, &t);
					if (r == ETIMEDOUT) {
						pthread_mutex_unlock(m);
						return false;
					}
					if (r != 0) {
						pthread_mutex_unlock(m);
						return false;
					}
				}
				(*cnt)--;
				pthread_mutex_unlock(m);
				return true;
			}
		#else
			struct timespec t;
			CalcTimeout(t, timeout);
			int r = sem_timedwait(semaphore, &t);
			return r == 0;
		#endif
	#endif
}

bool Semaphore::signal() {
	#ifdef WINDOWS
		return (ReleaseSemaphore( 
				semaphore,	// handle to semaphore
				1,		// increase count by one
				NULL) != 0);	// not interested in previous count
	#else
		#ifdef __APPLE__
			if (!name)
				return dispatch_semaphore_signal((dispatch_semaphore_t)semaphore_impl) != 0;
			else {
				char* data = (char*)semaphore_impl;
				if (!data) return false;
				pthread_mutex_t* m = (pthread_mutex_t*)data;
				pthread_cond_t* c = (pthread_cond_t*)(data + sizeof(pthread_mutex_t));
				uint32* cnt = (uint32*)(data + sizeof(pthread_mutex_t) + sizeof(pthread_cond_t));
				pthread_mutex_lock(m);
				(*cnt)++;
				pthread_cond_signal(c);
				pthread_mutex_unlock(m);
				return true;
			}
		#else
			int r = sem_post(semaphore);
			return (r == 0);
		#endif
	#endif
}

//bool Semaphore::destroy() {
//	#ifdef WINDOWS
//		return true;
//	#else
//		if (name)
//			sem_unlink(name);
//		return true;
//	#endif
//}













Event::Event() {
	name = NULL;
	#ifdef WINDOWS
		event = CreateEvent( 
			NULL,     // default security attributes
			TRUE,     // manual-reset event
			FALSE,    // initial state is nonsignaled
			NULL);	  // object name
	#else
		event = new pthread_cond_t;
		pthread_cond_init(event, NULL);

		mutex = new pthread_mutex_t;
		pthread_mutex_init(mutex, NULL);
	#endif
}

Event::Event(const char* name) {
	this->name = new char[MAXSHMEMNAMELEN];
	#ifdef WINDOWS
		//sprintf(this->name, "Global\\Event_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(this->name, MAXSHMEMNAMELEN, "%s\\Event_%s", WINSHMEMSPACE, name);
		// Creates or gets an existing Event
		event = CreateEvent( 
			NULL,     // default security attributes
			TRUE,     // manual-reset event
			FALSE,    // initial state is nonsignaled
			this->name);	  // object name
	#else
		char* data = OpenSharedMemorySegment(this->name, EVENTMEMSIZE);
		if (data) {
			mutex = (pthread_mutex_t*) data;
			event = (pthread_cond_t*) (data + sizeof(pthread_mutex_t));
		}
		else {
			// create shared event
			if (data = CreateSharedMemorySegment(this->name, EVENTMEMSIZE, true)) {
				// create mutex
				mutex = (pthread_mutex_t*) data;
				pthread_mutexattr_t attr;
				pthread_mutexattr_init(&attr);
				pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
				pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
				pthread_mutex_init(mutex, &attr);
				pthread_mutexattr_destroy(&attr);

				event = (pthread_cond_t*) (data + sizeof(pthread_mutex_t));
				pthread_condattr_t cattr;
				pthread_condattr_init(&cattr);
				pthread_condattr_setpshared(&cattr, PTHREAD_PROCESS_SHARED);
				// pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
				pthread_cond_init(event, &cattr);
				pthread_mutexattr_destroy(&attr);
			}
		}
	#endif
}

Event::~Event() {
	#ifdef WINDOWS
		if (event) {
			CloseHandle(event);
			event = NULL;
		}
		delete [] name;
	#else
		if (!name) {
			pthread_cond_destroy(event);
			delete(event);
			pthread_mutex_destroy(mutex);
			delete(mutex);
		}
		else {
			pthread_cond_destroy(event);
			pthread_mutex_destroy(mutex);
			CloseSharedMemorySegment((char*)mutex, EVENTMEMSIZE);
			mutex = NULL;
			event = NULL;
			delete [] name;
		}
	#endif
}

bool Event::waitNext() {
	#ifdef WINDOWS
		return (WaitForSingleObject(event, INFINITE) == WAIT_OBJECT_0);
	#else
		pthread_mutex_lock(mutex);
		int r = pthread_cond_wait(event, mutex);
		pthread_mutex_unlock(mutex);
		return r == 0;
	#endif
}

bool Event::waitNext(uint32 timeout) {
	#ifdef WINDOWS
		return (WaitForSingleObject(event, timeout) == WAIT_OBJECT_0);
	#else
		pthread_mutex_lock(mutex);
		struct timespec t;
		CalcTimeout(t, timeout);
		int r = pthread_cond_timedwait(event, mutex, &t);
		pthread_mutex_unlock(mutex);
		return r == 0;
	#endif
}

bool Event::signal() {
	#ifdef WINDOWS
		if (!SetEvent(event))
			return false;
//		Sleep(1);
		ResetEvent(event);
		return true;
	#else
		pthread_mutex_lock(mutex);
		int r = pthread_cond_broadcast(event);
		pthread_mutex_unlock(mutex);
		return (r == 0);
	#endif
}

//bool Event::destroy() {
//	#ifdef WINDOWS
//		return true;
//	#else
//		if (name)
//			sem_unlink(name);
//		return true;
//	#endif
//}














std::map<uint32, Timer*>* Timer::timers = NULL;

Timer::Timer() {
	if (!timers)
		timers = new std::map<uint32, Timer*>;
	globalID = 0;
	while (timers->find(globalID) != timers->end())
		globalID++;
	(*timers)[globalID] = this;
}

Timer::~Timer() {
	if (!timers)
		return;
	timers->erase(globalID);
	mutex.enter();
	std::map<uint32, TimerSchedule*>::iterator it = schedules.begin();
	std::map<uint32, TimerSchedule*>::iterator itEnd = schedules.end();
	while (it != itEnd)
		removeTimer((it++)->first);

	// Now delete the remaining old schedules that were kept
	// in case a timer returned the same time as it was removed
	std::list<TimerSchedule*>::iterator it2 = oldSchedules.begin();
	std::list<TimerSchedule*>::iterator it2End = oldSchedules.end();
	while (it2 != it2End)
		delete(*it2++);
	oldSchedules.clear();

	mutex.leave();
}

bool Timer::addTimer(uint32 id, uint32 interval, uint64 start, uint64 end) {
	mutex.enter();
	if (schedules[id]) {
		mutex.leave();
		return false;
	}

	TimerSchedule* schedule = new TimerSchedule;
	schedule->globalID = globalID;
	schedule->id = id;
	schedule->handle = 0;
	schedule->start = start;
	schedule->end = end;
	schedule->interval = interval;
	schedules[id] = schedule;

	uint64 now = GetTimeNow();
	uint32 firstDelay = 0;
	if (start && (start > now))
		firstDelay = (uint32)(start - now)/1000;

	// Create and set timer
	#ifdef WINDOWS
		if (!CreateTimerQueueTimer(&schedule->handle, NULL, TimerCallback, schedule, firstDelay, interval, WT_EXECUTEINTIMERTHREAD)) {
			schedules[id] = NULL;
			delete(schedule);
			mutex.leave();
			return false;
		}
	#else
		#ifdef __APPLE__
			dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
			dispatch_source_t timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
			if (!timer_source) {
				schedules[id] = NULL;
				delete(schedule);
				mutex.leave();
				return false;
			}
			schedule->handle = timer_source;
			uint64 firstDelayNs = (uint64)firstDelay * 1000000;
			uint64 intervalNs = (uint64)interval * 1000000;
			dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (int64_t)firstDelayNs), intervalNs, 0);
			dispatch_source_set_event_handler(timer_source, ^{
				if (!Timer::timers)
					return;
				TimerSchedule* sched = schedule;
				if (!sched)
					return;
				Timer* timer = (*Timer::timers)[sched->globalID];
				if (timer)
					timer->triggerTimer(sched->id);
			});
			dispatch_resume(timer_source);
		#else
			struct sigaction sa;
			struct sigevent timer_event;

			sigemptyset(&sa.sa_mask);
			sa.sa_flags = SA_SIGINFO;   /* Real-Time signal */
			sa.sa_sigaction = TimerCallback;
			sigaction(SIGRTMIN, &sa, NULL);

			timer_event.sigev_notify = SIGEV_SIGNAL;
			timer_event.sigev_signo = SIGRTMIN;
			timer_event.sigev_value.sival_ptr = (void *)schedule;
			if (timer_create(CLOCK_REALTIME, &timer_event, &schedule->handle) != 0) {
				schedules[id] = NULL;
				delete(schedule);
				mutex.leave();
				return false;
			}

			struct itimerspec newtv;
			sigset_t allsigs;

			uint64 period = interval * 1000;
			newtv.it_value.tv_sec = firstDelay / 1000000; 
			newtv.it_value.tv_nsec = (firstDelay % 1000000)*1000; 
			newtv.it_interval.tv_sec = period / 1000000; 
			newtv.it_interval.tv_nsec = (period % 1000000)*1000; 
		  
			if (timer_settime(schedule->handle, 0, &newtv, NULL) != 0) {
				timer_delete(schedule->handle);
				schedules[id] = NULL;
				delete(schedule);
				mutex.leave();
				return false;
			}
			sigemptyset(&allsigs);
		#endif
	#endif

	mutex.leave();
	return true;
}

bool Timer::removeTimer(uint32 id) {
	mutex.enter();

	TimerSchedule* schedule = schedules[id];
	if (!schedule) {
		mutex.leave();
		return true;
	}

	schedules[id] = NULL;

	#ifdef WINDOWS
		DeleteTimerQueueTimer(NULL, schedule->handle, NULL);
	#else
		#ifdef __APPLE__
			dispatch_source_cancel(schedule->handle);
			dispatch_release(schedule->handle);
		#else
			timer_delete(schedule->handle);
		#endif
	#endif

	oldSchedules.push_back(schedule);
	mutex.leave();
	return true;
}

bool Timer::triggerTimer(uint32 id) {
	mutex.enter();
	TimerSchedule* schedule = schedules[id];
	if (!schedule) {
		mutex.leave();
		return false;
	}

	TimerTrigger* trigger = new TimerTrigger;
	trigger->id = schedule->id;
	trigger->time = GetTimeNow();

	triggers.push(trigger);
	semaphore.signal();

	if ( schedule->end && ((int32)(schedule->end - trigger->time) < (int32)schedule->interval))
		removeTimer(schedule->id);

	mutex.leave();
	return true;
}

bool Timer::waitForTimer(uint32 timeout, uint32& id, uint64& time) {
	TimerTrigger* trigger;
	uint64 start = GetTimeNow();
	int64 timeleft;

	mutex.enter();
	while (!triggers.size()) {
		mutex.leave();
		if ( (timeleft = (int32)timeout - GetTimeAgeMS(start)) <= 0)
			return false;
		semaphore.wait((uint32)timeleft);
		mutex.enter();
	}

	trigger = triggers.front();
	triggers.pop();
	id = trigger->id;
	time = trigger->time;
	delete(trigger);
	mutex.leave();
	return true;
}

#ifdef WINDOWS
	void CALLBACK TimerCallback(PVOID arg, BOOLEAN TimerOrWaitFired) {
		if (!Timer::timers)
			return;
		TimerSchedule* schedule = (TimerSchedule*)arg;
		if (!schedule)
			return;
		Timer* timer = (*Timer::timers)[schedule->globalID];
		if (timer)
			timer->triggerTimer(schedule->id);
	}
#else
	#ifndef __APPLE__
	void TimerCallback(int sig, siginfo_t *siginfo, void *context) {
		if (!Timer::timers)
			return;
		TimerSchedule* schedule = (TimerSchedule*) siginfo->si_value.sival_ptr;
		if (!schedule)
			return;
		Timer* timer = (*Timer::timers)[schedule->globalID];
		if (timer)
			timer->triggerTimer(schedule->id);
	}
	#endif
#endif

bool UnitTest_Timer() {
	unittest::progress(5, "create timer");
	uint32 id;
	uint64 time, now = GetTimeNow();

	Timer* timer = new Timer();
	if (!timer) {
		unittest::fail("Timer test: failed to create Timer");
		return false;
	}

	unittest::progress(20, "schedule timers");
	if (!timer->addTimer(1, 1000, now+50000, now+10000000)) {
		unittest::fail("Timer test: addTimer 1 failed");
		delete(timer);
		return false;
	}
	if (!timer->addTimer(2, 2000, now+55000, now+15000000)) {
		unittest::fail("Timer test: addTimer 2 failed");
		delete(timer);
		return false;
	}
	if (!timer->addTimer(3, 3000, now+60000, now+20000000)) {
		unittest::fail("Timer test: addTimer 3 failed");
		delete(timer);
		return false;
	}

	unittest::progress(40, "wait for triggers");
	uint32 triggers = 0;
	double totalLatencyUs = 0;
	uint64 t0 = GetTimeNow();
	// Bounded loop: timers fire every 1-3ms after ~50ms, so this returns quickly.
	// Stop once we have collected a stable sample or the wall-clock budget elapses.
	for (uint32 n = 0; n < 60; n++) {
		if (timer->waitForTimer(100, id, time)) {
			now = GetTimeNow();
			triggers++;
			totalLatencyUs += (double)(now - time);
			unittest::detail("Timer %u triggered %.3f ms ago", id, (now - time) / 1000.0);
		}
		else {
			unittest::detail("(Timeout)");
		}
		// Keep the test well under a second of wall time.
		if ((GetTimeNow() - t0) > 2000000ULL)
			break;
	}

	unittest::progress(85, "verify triggers");
	if (triggers == 0) {
		unittest::fail("Timer test: no timer triggers received");
		delete(timer);
		return false;
	}

	unittest::metric("timer_triggers", (double)triggers, "count", true);
	unittest::metric("avg_trigger_latency", totalLatencyUs / (double)triggers, "us", false);

	delete(timer);
	unittest::progress(100, "done");
	return true;
}

bool UnitTest_Utils() {
	unittest::progress(5, "allocate bitfield");

	uint32 slotCount = 350;
	uint32 bitFieldSize = Calc32BitFieldSize(slotCount);
	char* bitField = new char[bitFieldSize];
	uint32 loc;

	// First set the full new bitfield to unoccupied
	Reset32BitField(bitField, bitFieldSize);
	unittest::detail("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());

	unittest::progress(15, "empty bitfield queries");
	if (GetLastOccupiedBitLoc(bitField, bitFieldSize, loc)) {
		unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: false", loc);
		delete[] bitField;
		return false;
	}

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 0)) {
		unittest::fail("GetFirstFreeBitLoc failed: %u, expected: 0", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 0)) {
		unittest::fail("GetFirstFreeBitLocN failed: %u, expected: 0", loc);
		delete[] bitField;
		return false;
	}

	unittest::progress(30, "single SetBit");
	if (!SetBit(10, BITOCCUPIED, bitField, bitFieldSize)) {
		unittest::fail("SetBit failed");
		delete[] bitField;
		return false;
	}
	unittest::detail("Bitfield: %s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 0)) {
		unittest::fail("GetFirstFreeBitLoc 2 failed: %u, expected: 0", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 0)) {
		unittest::fail("GetFirstFreeBitLocN 2 failed: %u, expected: 0", loc);
		delete[] bitField;
		return false;
	}

	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11)) {
		unittest::detail("Bitfield: %s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());
		unittest::fail("GetFirstFreeBitLocN 3 failed: %u, expected: 11", loc);
		delete[] bitField;
		return false;
	}
	if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 10)) {
		unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: 10", loc);
		delete[] bitField;
		return false;
	}

	unittest::progress(50, "SetBitN occupied ranges");
	if (!SetBit(0, BITOCCUPIED, bitField, bitFieldSize)) {
		unittest::fail("SetBit 2 failed");
		delete[] bitField;
		return false;
	}
	if (!SetBitN(100, 50, BITOCCUPIED, bitField, bitFieldSize)) {
		unittest::fail("SetBitN 100 failed");
		delete[] bitField;
		return false;
	}
	unittest::detail("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1)) {
		unittest::fail("GetFirstFreeBitLoc 4 failed: %u, expected: 1", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11)) {
		unittest::fail("GetFirstFreeBitLocN 5 failed: %u, expected: 11", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11)) {
		unittest::fail("GetFirstFreeBitLocN 6 failed: %u, expected: 11", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 150)) {
		unittest::fail("GetFirstFreeBitLocN 7 failed: %u, expected: 150", loc);
		delete[] bitField;
		return false;
	}
	if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 149)) {
		unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: 149", loc);
		delete[] bitField;
		return false;
	}

	if (!SetBitN(200, 50, BITOCCUPIED, bitField, bitFieldSize)) {
		unittest::fail("SetBitN 200 failed");
		delete[] bitField;
		return false;
	}
	unittest::detail("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());

	unittest::progress(70, "free-loc searches");
	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1)) {
		unittest::fail("GetFirstFreeBitLoc 8 failed: %u, expected: 1", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11)) {
		unittest::fail("GetFirstFreeBitLocN 9 failed: %u, expected: 11", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11)) {
		unittest::fail("GetFirstFreeBitLocN 10 failed: %u, expected: 11", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 250)) {
		unittest::fail("GetFirstFreeBitLocN 11 failed: %u, expected: 250", loc);
		delete[] bitField;
		return false;
	}
	if (GetFirstFreeBitLocN(bitField, bitFieldSize, 200, loc)) {
		unittest::fail("GetFirstFreeBitLocN 12 failed: %u, expected: false", loc);
		delete[] bitField;
		return false;
	}

	if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 249)) {
		unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: 249", loc);
		delete[] bitField;
		return false;
	}

	unittest::progress(85, "clear range and re-search");
	if (!SetBitN(210, 30, BITFREE, bitField, bitFieldSize)) {
		unittest::fail("SetBitN 210 failed");
		delete[] bitField;
		return false;
	}
	unittest::detail("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1)) {
		unittest::fail("GetFirstFreeBitLoc 13 failed: %u, expected: 1", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11)) {
		unittest::fail("GetFirstFreeBitLocN 14 failed: %u, expected: 11", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11)) {
		unittest::fail("GetFirstFreeBitLocN 15 failed: %u, expected: 11", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 52, loc) || (loc != 11)) {
		unittest::fail("GetFirstFreeBitLocN 16 failed: %u, expected: 11", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 250)) {
		unittest::fail("GetFirstFreeBitLocN 17 failed: %u, expected: 250", loc);
		delete[] bitField;
		return false;
	}
	if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 249)) {
		unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: 249", loc);
		delete[] bitField;
		return false;
	}

	if (!SetBitN(50, 30, BITOCCUPIED, bitField, bitFieldSize)) {
		unittest::fail("SetBitN 50 failed");
		delete[] bitField;
		return false;
	}
	unittest::detail("%s", PrintBitFieldString(bitField, bitFieldSize, slotCount, "").c_str());

	if (!GetFirstFreeBitLoc(bitField, bitFieldSize, loc) || (loc != 1)) {
		unittest::fail("GetFirstFreeBitLoc 18 failed: %u, expected: 1", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 10, loc) || (loc != 11)) {
		unittest::fail("GetFirstFreeBitLocN 19 failed: %u, expected: 11", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 11, loc) || (loc != 11)) {
		unittest::fail("GetFirstFreeBitLocN 20 failed: %u, expected: 11", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 52, loc) || (loc != 250)) {
		unittest::fail("GetFirstFreeBitLocN 21 failed: %u, expected: 250", loc);
		delete[] bitField;
		return false;
	}
	if (!GetFirstFreeBitLocN(bitField, bitFieldSize, 100, loc) || (loc != 250)) {
		unittest::fail("GetFirstFreeBitLocN 22 failed: %u, expected: 250", loc);
		delete[] bitField;
		return false;
	}
	if (GetFirstFreeBitLocN(bitField, bitFieldSize, 200, loc)) {
		unittest::fail("GetFirstFreeBitLocN 23 failed: %u, expected: false", loc);
		delete[] bitField;
		return false;
	}
	if (!GetLastOccupiedBitLoc(bitField, bitFieldSize, loc) || (loc != 249)) {
		unittest::fail("GetLastOccupiedBitLoc: loc %u, expected: 249", loc);
		delete[] bitField;
		return false;
	}

	delete[] bitField;
	unittest::progress(100, "done");
	return true;
}








// Create named shared memory segment
char* CreateSharedMemorySegment(const char* name, uint64 size, bool force) {

	char* memname = new char[MAXSHMEMNAMELEN];
	#ifdef WINDOWS
		//sprintf(memname, "Global\\Shmem_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(memname, MAXSHMEMNAMELEN, "%s\\Shmem_%s", WINSHMEMSPACE, name);
		HANDLE hMapFile;
		char* pBuf;

		hMapFile = CreateFileMapping(
			INVALID_HANDLE_VALUE,   // use paging file
			NULL,                   // default security 
			PAGE_READWRITE,         // read/write access
			(uint32)((size >> 32) & 0xffffffff),                      // maximum object size (high-order DWORD) 
			(uint32)(size & 0xffffffff),                   // maximum object size (low-order DWORD)  
			memname);                  // name of mapping object

		// Capture immediately: CreateFileMapping succeeds for an already-existing
		// named mapping and sets ERROR_ALREADY_EXISTS. On Windows a named mapping
		// lives until the last handle closes, so a segment left behind by a prior
		// in-process user (e.g. a previous unit test reusing system id 0) is handed
		// back with its OLD contents rather than fresh zeroed memory. Unlike the
		// POSIX/macOS branches - which honour `force` by unlinking and recreating -
		// this branch previously ignored `force`, so a forced create silently
		// reused stale state. Record whether it pre-existed so we can re-init below.
		bool alreadyExisted = (GetLastError() == ERROR_ALREADY_EXISTS);

		if (hMapFile == NULL) { 
			LogPrint(0,LOG_SYSTEM,0, "Could not create shared memory '%s': %s", memname, GetLastOSErrorMessage().c_str());
			delete [] memname;
			return NULL;
		}
		delete [] memname;
		pBuf = (char*) MapViewOfFile(hMapFile,   // handle to map object
			FILE_MAP_ALL_ACCESS, // read/write permission
			0, 0, 0);            // from beginning to end

		if (pBuf == NULL) { 
			int error = GetLastError(); 
			CloseHandle(hMapFile);
			return NULL;
		}

		// When the caller asked for a fresh segment (force) but Windows returned an
		// existing named mapping, zero the view so the caller gets clean memory -
		// matching the force semantics on POSIX/macOS. Without this, a forced
		// create over a leaked segment inherits stale occupancy/state, which is
		// exactly what made the CMSDK ProcessMemory tests fail on every other run.
		if (force && alreadyExisted)
			memset(pBuf, 0, (size_t)size);

		if (!SharedMemoryFileHandleMap)
			SharedMemoryFileHandleMap = new SharedMemoryFileHandleMapType;
		// Save the handle so we can close it later
		(*SharedMemoryFileHandleMap)[pBuf] = hMapFile;

		return pBuf;
	#else
		#ifdef __APPLE__
		/* macOS: use file-based shared memory (shm_open is unreliable, errno 63) */
		snprintf(memname, MAXSHMEMNAMELEN, "/tmp/Psyclone_Shmem_%s", name);
		if (force)
			unlink(memname);
		int fd = open(memname, O_CREAT | O_RDWR, 0666);
		if (fd == -1) {
			LogPrint(0,LOG_SYSTEM,0,"Couldn't create shared memory: '%s' (err: %d)...", memname, errno);
			delete [] memname;
			return NULL;
		}
		if (ftruncate(fd, size) == -1) {
			LogPrint(0,LOG_SYSTEM,0,"Couldn't truncate shared memory: '%s' size: %u (%d) (err: %d)...", memname, size, fd, errno);
			delete [] memname;
			close(fd);
			return NULL;
		}
		char* pBuf = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
		if (pBuf == MAP_FAILED) {
			LogPrint(0,LOG_SYSTEM,0,"Couldn't map created shared memory: '%s' (err: %d)...", memname, errno);
			delete [] memname;
			close(fd);
			return NULL;
		}
		if (!SharedMemoryFileHandleMap)
			SharedMemoryFileHandleMap = new SharedMemoryFileHandleMapType;
		(*SharedMemoryFileHandleMap)[pBuf] = std::string(memname);
		close(fd);
		delete [] memname;
		return pBuf;
		#else
		/* Linux: POSIX shm_open */
		snprintf(memname, MAXSHMEMNAMELEN, "/Shmem_%s", name);
		int fd = shm_open(memname, O_CREAT | O_RDWR, 0666);
		if (fd == -1) {
			LogPrint(0,LOG_SYSTEM,0,"Couldn't create shared memory: '%s' (err: %d)...", memname, errno);
			delete [] memname;
			return NULL;
		}
		if (ftruncate(fd, size) == -1) {
			LogPrint(0,LOG_SYSTEM,0,"Couldn't truncate shared memory: '%s' size: %u (%d) (err: %d)...", memname, size, fd, errno);
			delete [] memname;
			close(fd);
			return NULL;
		}
		char* pBuf = (char*)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
		if (pBuf == MAP_FAILED) {
			LogPrint(0,LOG_SYSTEM,0,"Couldn't map created shared memory: '%s' (err: %d)...", memname, errno);
			delete [] memname;
			close(fd);
			return NULL;
		}
		if (!SharedMemoryFileHandleMap)
			SharedMemoryFileHandleMap = new SharedMemoryFileHandleMapType;
		(*SharedMemoryFileHandleMap)[pBuf] = memname;
		close(fd);
		delete [] memname;
		return pBuf;
		#endif
	#endif
}

// Get named shared memory segment, size = 0 means autodetect size
char* OpenSharedMemorySegment(const char* name, uint64 size) {

	char* memname = new char[MAXSHMEMNAMELEN];
	#ifdef WINDOWS
		// sprintf(memname, "Global\\Shmem_%s_%u_%s", SharedSystemName, SharedSystemInstance, name);
		snprintf(memname, MAXSHMEMNAMELEN, "%s\\Shmem_%s", WINSHMEMSPACE, name);
		HANDLE hMapFile;
		char* pBuf;

		hMapFile = OpenFileMapping(
			FILE_MAP_ALL_ACCESS,   // read/write access
			FALSE,                 // do not inherit the name
			memname);                 // name of mapping object 

		if (hMapFile == NULL) { 
		//	LogPrint(0,LOG_SYSTEM,0, "Could not open shared memory '%s': %s", memname, GetLastOSErrorMessage().c_str());
			delete [] memname;
			return NULL;
		} 

		pBuf = (char*) MapViewOfFile(hMapFile, // handle to map object
			FILE_MAP_ALL_ACCESS,  // read/write permission
			0, 0, 0);             // from beginning to end

		if (pBuf == NULL) { 
			LogPrint(0,LOG_SYSTEM,0, "Could not map shared memory '%s': %s", memname, GetLastOSErrorMessage().c_str());
			delete [] memname;
			GetLastError(); 
			CloseHandle(hMapFile);
			return NULL;
		}
		delete [] memname;

		// Save the handle so we can close it later
		if (!SharedMemoryFileHandleMap)
			SharedMemoryFileHandleMap = new SharedMemoryFileHandleMapType;
		(*SharedMemoryFileHandleMap)[pBuf] = hMapFile;

		return pBuf;
	#else
		#ifdef __APPLE__
		/* macOS: open file-based shared memory */
		snprintf(memname, MAXSHMEMNAMELEN, "/tmp/Psyclone_Shmem_%s", name);
		int fd = open(memname, O_RDWR);
		if (fd == -1) {
			delete [] memname;
			return NULL;
		}
		char* pBuf;
		uint64 memSize = size;
		if (!size) {
			/* Autodetect: the segment file was ftruncate'd to its full size at
			   creation, so the on-disk file size IS the segment size. (Reading a
			   size prefix from offset 0 is wrong - callers store their own header
			   there, e.g. RequestServerHeader.) */
			struct stat st;
			if (fstat(fd, &st) == -1 || st.st_size <= 0) {
				delete [] memname;
				close(fd);
				return NULL;
			}
			memSize = (uint64) st.st_size;
		}
		pBuf = (char*)mmap(NULL, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
		if (pBuf == MAP_FAILED) {
			delete [] memname;
			close(fd);
			return NULL;
		}
		if (!SharedMemoryFileHandleMap)
			SharedMemoryFileHandleMap = new SharedMemoryFileHandleMapType;
		(*SharedMemoryFileHandleMap)[pBuf] = std::string(memname);
		close(fd);
		delete [] memname;
		return pBuf;
		#else
		/* Linux: POSIX shm_open */
		snprintf(memname, MAXSHMEMNAMELEN, "/Shmem_%s", name);
		int fd = shm_open(memname, O_RDWR, 0666);
		if (fd == -1) {
			delete [] memname;
			return NULL;
		}
		char* pBuf;
		uint64 memSize = size;
		if (!size) {
			/* Autodetect from the actual segment size (ftruncate'd at creation),
			   not from a size prefix at offset 0 - callers store their own header
			   there. */
			struct stat st;
			if (fstat(fd, &st) == -1 || st.st_size <= 0) {
				LogPrint(0,LOG_SYSTEM,0,"Couldn't stat opened shared memory: '%s' (err: %d)...", memname, errno);
				delete [] memname;
				close(fd);
				return NULL;
			}
			memSize = (uint64) st.st_size;
		}
		pBuf = (char*)mmap(NULL, memSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
		if (pBuf == MAP_FAILED) {
			delete [] memname;
			close(fd);
			return NULL;
		}
		if (!SharedMemoryFileHandleMap)
			SharedMemoryFileHandleMap = new SharedMemoryFileHandleMapType;
		(*SharedMemoryFileHandleMap)[pBuf] = memname;
		close(fd);
		delete [] memname;
		return pBuf;
		#endif
	#endif
}

// Close named shared memory segment
bool CloseSharedMemorySegment(char* data, uint64 size) {
	if (!data || !size)
		return true;
	#ifdef WINDOWS
		UnmapViewOfFile(data);
		CloseHandle((*SharedMemoryFileHandleMap)[data]);
		SharedMemoryFileHandleMap->erase(data);
		return true;
	#else
		munmap(data, size);
		#ifdef __APPLE__
		unlink((*SharedMemoryFileHandleMap)[data].c_str());
		#else
		shm_unlink((*SharedMemoryFileHandleMap)[data].c_str());
		#endif
		SharedMemoryFileHandleMap->erase(data);
		return true;
	#endif
}

// Destroy named shared memory segment
//bool DestroySharedMemorySegment(char* data, uint32 size) {
//	#ifdef WINDOWS
//		UnmapViewOfFile(data);
//		CloseHandle(SharedMemoryFileHandleMap[data]);
//		SharedMemoryFileHandleMap[data] = NULL;
//		return true;
//	#else
//		munmap(data, size);
//		close(SharedMemoryFileHandleMap[data]);
//		SharedMemoryFileHandleMap[data] = NULL;
//		return (shm_unlink(data) == 0);
//	#endif
//}






uint64 ntoh64(const uint64 *input) {
	uint64 rval;
	uint8 *data = (uint8*)&rval;
	data[0] = (uint8)(*input >> 56);
	data[1] = (uint8)(*input >> 48);
	data[2] = (uint8)(*input >> 40);
	data[3] = (uint8)(*input >> 32);
	data[4] = (uint8)(*input >> 24);
	data[5] = (uint8)(*input >> 16);
	data[6] = (uint8)(*input >> 8);
	data[7] = (uint8)(*input >> 0);
	return rval;
}

uint64 hton64(const uint64 *input) {
	return (ntoh64(input));
}




uint32 Calc32BitFieldSize(uint32 bitsize) {
	return ((bitsize + 31) >> 5) << 2;
}

bool Reset32BitField(char* bitfield, uint32 bytesize) {
	memset(bitfield, 255, bytesize);
	return true;
}

// Get first zero bit in field of size bits, starting from data loc
bool GetFirstFreeBitLoc(const char* bitfield, uint32 bytesize, uint32& loc) {
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)data;
	int32 index;

	while ((*src == 0)) {
		src++;
		if (src - data > (int32)bytesize)
			return false;
	}
	#if defined	WINDOWS
		_BitScanForward((DWORD*)&index, *src);
	#elif defined LINUX
		index = ffsl(*src) - 1;
	#elif defined OSX
		index = ffsl(*src) - 1;
	#endif

	loc = ((uint32)(src-data)*32) + index;
	return true;
}

// Get first block of num zero bit in field of size bits, starting from data loc
bool GetFirstFreeBitLocN(const char* bitfield, uint32 bytesize, uint32 num, uint32& loc) {
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)data;
	int32 index;

	uint32 l;
	bit val;

	uint32 foundBlock = 0;
	while (true) {
		while ((*src == 0)) {
			src++;
			if (src - data > (int32)bytesize)
				return false;
		}
		#if defined	WINDOWS
			_BitScanForward((DWORD*)&index, *src);
		#elif defined LINUX
			index = ffsl(*src) - 1;
		#elif defined OSX
			index = ffsl(*src) - 1;
		#endif
		loc = ((uint32)(src - data) * 32) + index;
		l = loc + 1;
		foundBlock = 1;
		// check for num sized block until the end of the next int32 boundary (inline bit read)
		if ((l + 7) >> 3 > bytesize)
			return false;
		{
			uint32* word = (uint32*)bitfield + (l >> 5);
			uint32 n = l & 31;
			#if defined WINDOWS
				val = (_bittest((long*)word, n) != 0);
			#elif defined LINUX
				val = (bit)((*word & (static_cast<uint32>(1) << n)) != 0);
			#elif defined OSX
				val = (bit)((*word & (static_cast<uint32>(1) << n)) != 0);
			#endif
		}
		while (true) {
			l++;
			index++;
			if (val == BITFREE) {
				foundBlock++;
				if (foundBlock >= num)
					return true;
				if (index % 32 == 0) {
					while ((num - foundBlock > 31) && (*src == MAXVALUINT32)) {
						foundBlock += 32;
						l += 32;
						src++;
					}
					if ((num - foundBlock > 15) && (*(uint16*)src == MAXVALUINT16)) {
						foundBlock += 16;
						l += 16;
						index += 16;
						if ((num - foundBlock > 7) && (*((unsigned char*)src+2) == 255)) {
							foundBlock += 8;
							l += 8;
							index += 8;
						}
					}
					else if ((num - foundBlock > 7) && (*(unsigned char*)src == 255)) {
						foundBlock += 8;
						l += 8;
						index += 8;
					}
				}
				if (foundBlock >= num)
					return true;
			}
			else {
				foundBlock = 0;
				loc = l;
				// are we on a boundary
				if (index % 32 == 0)
					break;
			}
			if ((l + 7) >> 3 > bytesize)
				return false;
			{
				uint32* word = (uint32*)bitfield + (l >> 5);
				uint32 n = l & 31;
				#if defined WINDOWS
					val = (_bittest((long*)word, n) != 0);
				#elif defined LINUX
					val = (bit)((*word & (static_cast<uint32>(1) << n)) != 0);
				#elif defined OSX
					val = (bit)((*word & (static_cast<uint32>(1) << n)) != 0);
				#endif
			}
		}
		// we are now on a boundary, jump back and use the bitscan search
		src += (uint32)(index / 32);
		index = 0;
		// if we get here, the block found wasn't big enough, continue
	}
	return true;
}

// Set the nth bit to value in field of size bits, starting from data loc
bool SetBit(uint32 loc, bit value, char* bitfield, uint32 bytesize) {
	if ((loc + 7) >> 3 > bytesize)
		return false;
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)data;
	uint32 n = loc;
	while (n > 31) {
		n -= 32;
		src++;
	}

	#if defined	WINDOWS
		if (value != 0)
			_bittestandset((long*)src, n);
		else
			_bittestandreset((long*)src, n);
	#elif defined LINUX
		if (value != 0)
			*src |= 1<<n;
		else
			*src &= ~(1<<n);
			//*src &= ((1<<n)^0xFFFFFFFF);
	#elif defined OSX
		if (value != 0)
			*src |= 1<<n;
		else
			*src &= ~(1<<n);
	#endif
	return true;
}

// Set the block of num bits starting with nth bit to value in field of size bits, starting from data loc
bool SetBitN(uint32 loc, uint32 num, bit value, char* bitfield, uint32 bytesize) {
	if ((loc + 7) >> 3 > bytesize)
		return false;
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)data;
	uint32 n = loc;
	while (n > 31) {
		n -= 32;
		src++;
	}

	for (uint32 i = 0; i < num; i++) {
		if (!n) {
			while (num - i > 31) {
				*src = (value ? MAXVALUINT32 : 0);
				i += 32;
				src++;
			}
			if (num - i > 15) {
				*(uint16*)src = (value ? MAXVALUINT16 : 0);
				i += 16;
				n += 16;
				if (num - i > 7) {
					*((unsigned char*)src + 2) = (value ? 255 : 0);
					i += 8;
					n += 8;
				}
			}
			else if (num - i > 7) {
				*(unsigned char*)src = (value ? 255 : 0);
				i += 8;
				n += 8;
			}
			if (i == num)
				return true;
		}
		#if defined	WINDOWS
			if (value != 0)
				_bittestandset((long*)src, n);
			else
				_bittestandreset((long*)src, n);
		#elif defined LINUX
			if (value != 0)
				*src |= 1 << n;
			else
				*src &= ~(1 << n);
			//*src &= ((1<<n)^0xFFFFFFFF);
		#elif defined OSX
			if (value != 0)
				*src |= 1 << n;
			else
				*src &= ~(1 << n);
		#endif
		n++;
		if (n > 31) {
			n = 0;
			src++;
		}
	}
	return true;
}

// Get the nth bit in field of size bits, starting from data loc
bool GetBit(uint32 loc, const char* bitfield, uint32 bytesize, bit& val) {
	if ((loc + 7) >> 3 > bytesize)
		return false;
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)data;
	uint32 n = loc;
	while (n > 31) {
		n -= 32;
		src++;
	}

	#if defined	WINDOWS
		val = (_bittest((long*)src, n) != 0);
	#elif defined LINUX
		uint32 mask = static_cast<uint32>( 1 << n ) ;
		val = (bit)(mask & *src);
	#elif defined OSX
		uint32 mask = static_cast<uint32>(1 << n);
		val = (bit)(mask & *src);
	#endif
//	printf("GetBit returned %u...\n", val);
	return true;
}

// Get location of last bit in use, starting from data loc
bool GetLastOccupiedBitLoc(const char* bitfield, uint32 bytesize, uint32& loc) {
	uint32* data = (uint32*)bitfield;
	uint32* src = (uint32*)(bitfield + bytesize - 4);
	int32 index;

	loc = 0;
	while ((*src == 0xFFFFFFFF)) {
		if (src <= data)
			return false;
		src--;
	}

	#if defined	WINDOWS
		_BitScanReverse((DWORD*)&index, ~(*src));
	#elif defined LINUX
		index = 31 - __builtin_clz(~(*src));
	#elif defined OSX
		index = 31 - __builtin_clz(~(*src));
	#endif
	loc = ((uint32)(src - data) * 32) + index;
	return true;
}


// Return string representing the bitfield
std::string GetBitFieldAsString(const char* bitfield, uint32 bytesize, uint32 size) {
	std::string str;
	str.reserve(bytesize * 9);

	uint32 i,j,n=0;
	for(i = 0; i < bytesize; i++) {
		for(j = 0; j < 8; j++) {
			if (++n <= size)
				str.push_back((bitfield[i] & (1 << j)) ? '_' : '0');
			else
				str.push_back('*');
		}
		str.push_back(' ');
	};
	return str;
}


// Print string representing the bitfield
std::string PrintBitFieldString(const char* bitfield, uint32 bytesize, uint32 size, const char* title) {
	std::string str = GetBitFieldAsString(bitfield, bytesize, size);
	uint32 strSize = (uint32)str.size();
	if (!strSize)
		return "";
	if (strSize < 73) {
		if (title != NULL)
			return StringFormat("%s [%u]: %s\n", title, size, str.c_str());
		else
			return StringFormat("%s\n", str.c_str());
	}
	else {
		std::string str2;
		if (title != NULL)
			str2 = StringFormat("%s [%u]:\n", title, size);
		uint32 loc = 0;
		while (loc < strSize-1) {
			str2 += str.substr(loc, 36) + "\n";
			loc += 36;
		}
		if (loc < strSize)
			str2 += str.substr(loc) + "\n";
		return str2;
	}
}




int32 AtomicIncrement32(int32 volatile &v) {
	#if defined	WINDOWS
		return	InterlockedIncrement((LONG*)&v);
	// #elif defined LINUX
	#else
		__sync_add_and_fetch(&v, 1);
		return	v;
	#endif
};

int64 AtomicIncrement64(int64 volatile &v) {
	#if defined	WINDOWS
		return	InterlockedIncrement64((LONGLONG*)&v);
	// #elif defined LINUX
	#else
		__sync_add_and_fetch(&v, 1);
		return	v;
	#endif
};

int32 AtomicDecrement32(int32 volatile &v) {
	#if defined	WINDOWS
		return	InterlockedDecrement((LONG*)&v);
	// #elif defined LINUX
	#else
		__sync_add_and_fetch(&v, -1);
		return	v;
	#endif
};

int64 AtomicDecrement64(int64 volatile &v) {
	#if defined	WINDOWS
		return	InterlockedDecrement64((LONGLONG*)&v);
	// #elif defined LINUX
	#else
		__sync_add_and_fetch(&v, -1);
		return	v;
	#endif
};

bool Sleep(uint32 ms) {
	#if defined	WINDOWS
		// we are actually being passed millisecond, so multiply up
		::Sleep((uint32)ms);
	#else
		usleep(ms*1000);
	#endif
	return true;
}

std::string PrintProgramTrace(uint32 startLine, uint32 endLine) {
	std::string tracePrint;
	std::list<std::string> trace = GetProgramTrace();
	
	std::list<std::string>::iterator i = trace.begin(), e = trace.end();

	uint32 line = 0;
	while (i != e) {
		if ((++line > startLine) && (!endLine || (line <= endLine)))
			tracePrint += " -" + (*i) + "\n";
		i++;
	}
	return tracePrint;
}

std::list<std::string> GetProgramTrace() {
	std::list<std::string> trace;
	#ifdef WINDOWS
		// this needs Dbghelp.h included and link with Dbghelp.lib
		//unsigned int   i;
		//void         * stack[ 100 ];
		//unsigned short frames;
		//SYMBOL_INFO  * symbol;
		//HANDLE         process;

		//process = GetCurrentProcess();

		//SymInitialize( process, NULL, TRUE );

		//frames               = CaptureStackBackTrace( 0, 100, stack, NULL );
		//symbol               = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 256 * sizeof( char ), 1 );
		//symbol->MaxNameLen   = 255;
		//symbol->SizeOfStruct = sizeof( SYMBOL_INFO );

		//for( i = 0; i < frames; i++ ) {
		//	SymFromAddr( process, ( DWORD64 )( stack[ i ] ), 0, symbol );
		//	trace.push_back(StringFormat("%i: %s   (0x%0X)", frames - i - 1, symbol->Name, symbol->Address));
		//}
		//free( symbol );
	#else
		#ifdef _DEBUG
			void *array[200];
			size_t size;
			char **strings;
			size_t i;
			size = backtrace(array, 200);
			strings = backtrace_symbols(array, size);
			for (i = 0; i < size; i++)
				trace.push_back(strings[i]);
			free (strings);
		#endif // _DEBUG
	#endif
	return trace;
}


bool CreateThread(THREAD_FUNCTION func, void* args, ThreadHandle& thread, uint32 &osID) {

	#if defined	WINDOWS
		DWORD dwThreadId;
		thread = ::CreateThread( 
			NULL,							// no security attributes 
			0,								// use default stack size  
			(LPTHREAD_START_ROUTINE) func,	// thread function 
			args,							// argument to thread function 
			0,								// use default creation flags 
			&dwThreadId);					// returns the thread identifier 

		if (thread != NULL) {
			osID = ::GetThreadId(thread);
			Sleep(0);
			return true;
		}
		else
			return false;
	#else
		pthread_attr_t attr;
		pthread_attr_init(&attr); /* initialize attr with default attributes */
		// pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
		pthread_attr_setschedpolicy(&attr, SCHED_RR);
		pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
		// pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);

		int res;
		if ((res=pthread_create(&thread, &attr, func, args)) != 0)
			return false;
		int oldstate;
		pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &oldstate);
		pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldstate);
		#ifdef __APPLE__
			uint64 tid;
			pthread_threadid_np(thread, &tid);
			osID = (uint32)tid;
		#else
			osID = (uint32) thread;
		#endif
		// printf("create hThread: %u\n", thread);
		Sleep(0);
		return true;
	#endif
}

bool CheckForThreadFinished(ThreadHandle hThread) {
	#if defined	WINDOWS
		int res = WaitForSingleObject(hThread, 0);
		if (res != WAIT_OBJECT_0)
			return false;
		return true;
		//return (CloseHandle(hThread) != 0);
	#else
		#ifdef __APPLE__
			// Non-destructive probe -- see IsThreadRunning. Must not pthread_join here,
			// or the later authoritative join double-joins a reaped thread and corrupts
			// the heap. pthread_kill(h, 0): ESRCH (non-zero) => thread has finished.
			// A null handle (empty slot) is "finished"; guard it since pthread_kill,
			// unlike pthread_join, dereferences the handle and would crash on null.
			if (!hThread)
				return true;
			return (pthread_kill(hThread, 0) != 0);
		#else
			// Non-destructive liveness probe (same rationale as the __APPLE__ branch).
			// The previous pthread_tryjoin_np() REAPS the thread on success, so any
			// second probe of the same handle (e.g. ThreadManager::shutdown()'s wait
			// loop followed by terminateThread()'s grace loop) operated on an
			// already-joined pthread_t -- undefined behaviour that segfaulted inside
			// pthread_tryjoin_np (SIGSEGV in the CMSDK threadmanager/network tests).
			// pthread_kill(h, 0) sends no signal: 0 => alive, ESRCH => finished.
			// A null handle (empty/reaped slot) counts as finished. Reaping is done
			// exactly once via utils::ReapThread / WaitForThreadToFinish / TerminateThread.
			if (!hThread)
				return true;
			return (pthread_kill(hThread, 0) != 0);
		#endif
	#endif
}

bool ReapThread(ThreadHandle& hThread) {
	// Release the OS resources of a thread that has ALREADY finished, exactly
	// once, and zero the handle so no later probe/join can touch it again.
	// POSIX: a joinable thread that exited keeps its stack until pthread_join.
	// Windows: the thread object lives until its handle is closed.
	if (!hThread)
		return true;
	#if defined WINDOWS
		CloseHandle(hThread);
	#else
		pthread_join(hThread, NULL);
	#endif
	hThread = 0;
	return true;
}

bool TryReapThread(ThreadHandle& hThread) {
	// Atomic probe-and-reap: returns true iff the thread has finished, and in
	// that case releases its OS resources exactly once and ZEROES the handle so
	// no later probe, join or cancel can ever touch a dead pthread_t again
	// (double pthread_tryjoin_np/pthread_join on one handle is undefined
	// behaviour and segfaulted the CMSDK threadmanager/network tests on Linux).
	// Note: pthread_kill(h, 0) cannot be used as the probe on glibc >= 2.34 --
	// it returns 0 ("alive") for a thread that exited but was not yet joined.
	if (!hThread)
		return true;
	#if defined WINDOWS
		if (WaitForSingleObject(hThread, 0) != WAIT_OBJECT_0)
			return false;
		CloseHandle(hThread);
	#elif defined __APPLE__
		if (pthread_kill(hThread, 0) == 0)
			return false;      // still alive
		pthread_join(hThread, NULL);
	#else
		if (pthread_tryjoin_np(hThread, NULL) != 0)
			return false;      // still alive (EBUSY)
	#endif
	hThread = 0;
	return true;
}

bool WaitForThreadToFinish(ThreadHandle hThread, uint32 timeoutMS) {
	// Sadly, the Linux implementation of join doesn't allow a timeout, so to be compatible
	// we cannot allow the Windows to have this either, although it could (and should) have
	#if defined	WINDOWS
		int res = WaitForSingleObject(hThread, timeoutMS ? timeoutMS : INFINITE);
		if(res != WAIT_OBJECT_0)
			return false;
		return true;
		//return (CloseHandle(hThread) != 0);
	#else
		#ifdef __APPLE__
			if (!hThread)
				return true; // nothing to wait for
			// macOS has no pthread_timedjoin_np. A timeout==0 means "block until done".
			if (timeoutMS == 0)
				return (pthread_join(hThread, NULL) == 0);
			// For a bounded wait, poll liveness non-destructively (pthread_kill, no
			// signal) until the thread exits or the deadline passes, then perform a
			// single authoritative join to reap it. This avoids spawning a detached
			// joiner thread that would race a later join on the same handle (the
			// previous approach could leave two pthread_join calls on one target --
			// undefined behaviour that corrupted the heap).
			uint32 waited = 0;
			while (pthread_kill(hThread, 0) == 0) {
				if (waited >= timeoutMS)
					return false; // still running at deadline; do NOT reap
				Sleep(2);
				waited += 2;
			}
			// Thread has exited but is still joinable; reap it exactly once.
			pthread_join(hThread, NULL);
			return true;
		#else
			struct timespec ts;
			CalcTimeout(ts, timeoutMS);
			if (timeoutMS) {
				if (pthread_timedjoin_np(hThread, NULL, &ts) != 0)
					return false;
				else
					return true;
			}
			else {
				if (pthread_join(hThread, NULL) != 0)
					return false;
			}
			return true;
		#endif
	#endif
}

bool TerminateThread(ThreadHandle hThread) {
	if (!hThread)
		return true;
	#if defined	WINDOWS
		// printf("cancel hThread: %u\n", hThread);
		bool res = false;
		try {
			res = (::TerminateThread(hThread, 0) != 0);
			if (!res) {
				char* msg = new char[2048];
				int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
					NULL, GetLastError(), 0, msg, sizeof(msg), NULL);
				msg[length] = '\0';
				LogPrint(0, LOG_SYSTEM, 0, "Error terminating thread: %s", msg);
				delete[] msg;
			}
			else 
				CloseHandle(hThread);
		}
		catch (...) {}
		return res;
	#else
		// printf("cancel hThread: %u\n", hThread);
		pthread_cancel(hThread);
		if (pthread_join(hThread, NULL) != 0)
			return false;
		return true;
	#endif
}

bool PauseThread(ThreadHandle hThread) {
	#if defined	WINDOWS
		return (SuspendThread(hThread) >= 0);
	#else
		return (pthread_kill(hThread, SIGSTOP) == 0);
	#endif
	return false;
}

bool ContinueThread(ThreadHandle hThread) {
	#if defined	WINDOWS
		DWORD count;
		while ( (count = ResumeThread(hThread)) > 1);
		return (count == 1);
	#else
		return (pthread_kill(hThread, SIGCONT) == 0);
	#endif
	return false;
}

bool GetCurrentThreadUniqueID(uint32 &tid) {
	// This function returns an ID unique to the thread on a given OS
	// On Windows this is the Thread OS ID
	// On Linux we cannot read other thread OS IDs so it has to be the
	// thread handle converted to a 32bit uint.
	#if defined	WINDOWS
		tid = ::GetCurrentThreadId();
		return true;
	#else
		#ifdef __APPLE__
			uint64 id;
			pthread_threadid_np(pthread_self(), &id);
			tid = (uint32)id;
		#else
			tid = (uint32) pthread_self();
		#endif
		return true;
	#endif
}

bool GetCurrentThreadOSID(uint32 &tid) {
	#if defined	WINDOWS
		tid = ::GetCurrentThreadId();
		return true;
	#else
		#ifdef __APPLE__
			uint64 id;
			if (pthread_threadid_np(pthread_self(), &id) != 0)
				return false;
			tid = (uint32)id;
		#else
			int id = syscall(SYS_gettid);
			if (id <= 0)
				return false;
			tid = (uint32)id;
		#endif
		return true;
	#endif
}

bool GetCurrentThread(ThreadHandle& thread) {
	#if defined	WINDOWS
		thread = ::GetCurrentThread();
		return true;
	#else
		thread = pthread_self();
		return true;
	#endif
}

bool IsThreadRunning(ThreadHandle hThread) {
	#if defined	WINDOWS
		DWORD code;
		if (!GetExitCodeThread(hThread, &code))
			return false;
		return (code == STILL_ACTIVE);
	#else
		#ifdef __APPLE__
			// Non-destructive liveness probe. macOS has no pthread_tryjoin_np, and we
			// must NOT use pthread_join here: this is polled repeatedly (e.g. by the
			// ThreadManager monitoring loop) and a join reaps the thread, after which
			// the authoritative TerminateThread/WaitForThreadToFinish join would operate
			// on an already-joined pthread_t -- undefined behaviour that corrupts the
			// heap. pthread_kill(h, 0) sends no signal and just reports existence:
			// 0 => still alive, ESRCH => already terminated. Unlike pthread_join, it
			// dereferences the handle, so a null handle (empty slot) must be guarded.
			if (!hThread)
				return false;
			return (pthread_kill(hThread, 0) == 0);
		#else
			// Non-destructive probe -- see CheckForThreadFinished/__APPLE__ comments.
			// pthread_tryjoin_np reaped the thread on success, making any later join
			// or probe of the same handle undefined behaviour (heap corruption/SIGSEGV).
			if (!hThread)
				return false;
			return (pthread_kill(hThread, 0) == 0);
		#endif
	#endif
}

uint32 GetThreadStatColAbility() {
	#if defined	WINDOWS
		return THREAD_STATS_AUTO;
	#else
		#if defined RUSAGE_THREAD
			return THREAD_STATS_ADHOC;
		#else
			return THREAD_STATS_OFF;
		#endif
	#endif
}

bool GetCPUTicks(ThreadHandle hThread, uint64& ticks) {
	ticks = 0;

	#if defined	WINDOWS
	//	if (!QueryThreadCycleTime(hThread, &ticks))
	//		return false;
		uint64 cpuTicks = 0;
		LARGE_INTEGER perfFreq;
		if (!QueryPerformanceFrequency(&perfFreq) || !perfFreq.QuadPart)
			return false;
		if (!QueryThreadCycleTime(hThread, &cpuTicks))
			return false;
		ticks = cpuTicks / (perfFreq.QuadPart / 1000000); // cpu ticks / (number of ticks per us)
														  //FILETIME creationTime;
		//FILETIME exitTime;
		//FILETIME kernelTime;
		//FILETIME userTime;
		//if (!GetThreadTimes(hThread, &creationTime, &exitTime, &kernelTime, &userTime))
		//	return false;
		//userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
		//kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
		return true;
	#else
		#ifdef __APPLE__
			// macOS: use Mach thread_info to get per-thread CPU time
			mach_port_t machThread = pthread_mach_thread_np(hThread);
			if (machThread == MACH_PORT_NULL)
				return false;
			thread_basic_info_data_t info;
			mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT;
			if (thread_info(machThread, THREAD_BASIC_INFO, (thread_info_t)&info, &infoCount) != KERN_SUCCESS)
				return false;
			ticks = (uint64)info.user_time.seconds * 1000000 + info.user_time.microseconds
				  + (uint64)info.system_time.seconds * 1000000 + info.system_time.microseconds;
			return true;
		#else
			clockid_t cid;
			if (pthread_getcpuclockid(hThread, &cid) != 0)
				return false;
			timespec ts;
			if (clock_gettime(cid, &ts) != -1 ) {
				ticks = ((uint64)ts.tv_sec * 1000000) + (ts.tv_nsec/1000);
				return true;
			}
			else
				return false;
		#endif
	#endif
	return true;
}

bool GetCPUTicks(uint64& ticks) {
	ticks = 0;

	#if defined	WINDOWS
		// #### QueryPerformanceFrequency
		uint64 cpuTicks = 0;
		LARGE_INTEGER perfFreq;
		if (!QueryPerformanceFrequency(&perfFreq) || !perfFreq.QuadPart)
			return false;
		if (!QueryThreadCycleTime(::GetCurrentThread(), &cpuTicks))
			return false;
		ticks = cpuTicks / (perfFreq.QuadPart / 1000000); // cpu ticks / (number of ticks per us)
		//FILETIME creationTime;
		//FILETIME exitTime;
		//FILETIME kernelTime;
		//FILETIME userTime;
		//if (!GetThreadTimes(::GetCurrentThread(), &creationTime, &exitTime, &kernelTime, &userTime))
		//	return false;
		//userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
		//kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
		return true;
	#else
		timespec ts;
		if (clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts) != -1 ) {
			ticks = ((uint64)ts.tv_sec * 1000000) + (ts.tv_nsec/1000);
			return true;
		}
		else
			return false;
		//#if defined RUSAGE_THREAD
		//	struct rusage us;
		//	if (getrusage(RUSAGE_THREAD, &us) == 0) { // RUSAGE_THREAD // accumulate_thread_rusage
		//		userCPU = (us.ru_utime.tv_sec * 1000000) + us.ru_utime.tv_usec;
		//		kernelCPU = (us.ru_stime.tv_sec * 1000000) + us.ru_stime.tv_usec;
		//	}
		//	else
		//		return false;
		//#else
		//	return false;
		//#endif
	#endif
}

bool GetProcessCPUTicks(uint32 osProcID, uint64& ticks) {
	ticks = 0;

	#if defined	WINDOWS
		HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, osProcID);
		if (!hProcess)
			return false;
	//	if (!QueryProcessCycleTime(hProcess, &ticks))
	//		return false;

		uint64 cpuTicks = 0;
		LARGE_INTEGER perfFreq;
		if (!QueryPerformanceFrequency(&perfFreq) || !perfFreq.QuadPart)
			return false;
		if (!QueryProcessCycleTime(hProcess, &cpuTicks))
			return false;
		ticks = cpuTicks / (perfFreq.QuadPart / 1000000); // cpu ticks / (number of ticks per us)

		//FILETIME creationTime;
		//FILETIME exitTime;
		//FILETIME kernelTime;
		//FILETIME userTime;
		//if (!GetProcessTimes(::GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime))
		//	return false;
		//userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
		//kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
		return true;
	#else
		#ifdef __APPLE__
			if ((pid_t)osProcID != getpid())
				return false;
			struct rusage ru;
			if (getrusage(RUSAGE_SELF, &ru) != 0)
				return false;
			ticks = ((uint64)ru.ru_utime.tv_sec * 1000000) + (uint64)ru.ru_utime.tv_usec
				+ ((uint64)ru.ru_stime.tv_sec * 1000000) + (uint64)ru.ru_stime.tv_usec;
			return true;
		#else
			clockid_t clockid;
			if (clock_getcpuclockid(osProcID, &clockid) != 0)
				return false;
			timespec ts;
			if (clock_gettime(clockid, &ts) != -1 ) {
				ticks = ((uint64)ts.tv_sec * 1000000) + (ts.tv_nsec/1000);
				return true;
			}
			else
				return false;
			//#if defined RUSAGE_THREAD
		//	struct rusage us;
		//	if (getrusage(RUSAGE_SELF, &us) == 0) { // RUSAGE_THREAD // accumulate_thread_rusage
		//		userCPU = (us.ru_utime.tv_sec * 1000000) + us.ru_utime.tv_usec;
		//		kernelCPU = (us.ru_stime.tv_sec * 1000000) + us.ru_stime.tv_usec;
		//	}
		//	else
		//		return false;
		//#else
		//	return false;
		//#endif
		#endif
	#endif
	return true;
}

bool GetProcessCPUTicks(uint64& ticks) {
	ticks = 0;

	#if defined	WINDOWS
	//	if (!QueryProcessCycleTime(::GetCurrentProcess(), &ticks))
	//		return false;

		uint64 cpuTicks = 0;
		LARGE_INTEGER perfFreq;
		if (!QueryPerformanceFrequency(&perfFreq) || !perfFreq.QuadPart)
			return false;
		if (!QueryProcessCycleTime(::GetCurrentProcess(), &cpuTicks))
			return false;
		ticks = cpuTicks / (perfFreq.QuadPart / 1000000); // cpu ticks / (number of ticks per us)

		//FILETIME creationTime;
		//FILETIME exitTime;
		//FILETIME kernelTime;
		//FILETIME userTime;
		//if (!GetProcessTimes(::GetCurrentProcess(), &creationTime, &exitTime, &kernelTime, &userTime))
		//	return false;
		//userCPU = ((uint64)userTime.dwLowDateTime | (((uint64)userTime.dwHighDateTime) << 32)) / 10;
		//kernelCPU = ((uint64)kernelTime.dwLowDateTime | (((uint64)kernelTime.dwHighDateTime) << 32)) / 10;
		return true;
	#else
		timespec ts;
		if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts) != -1 ) {
			ticks = ((uint64)ts.tv_sec * 1000000) + (ts.tv_nsec/1000);
			return true;
		}
		else
			return false;
		//#if defined RUSAGE_THREAD
		//	struct rusage us;
		//	if (getrusage(RUSAGE_SELF, &us) == 0) { // RUSAGE_THREAD // accumulate_thread_rusage
		//		userCPU = (us.ru_utime.tv_sec * 1000000) + us.ru_utime.tv_usec;
		//		kernelCPU = (us.ru_stime.tv_sec * 1000000) + us.ru_stime.tv_usec;
		//	}
		//	else
		//		return false;
		//#else
		//	return false;
		//#endif
	#endif
	return true;
}

bool GetProcAndOSCPUUsage(double& procPercentUse, double& osPercentUse, uint64 &prevOSTicks, uint64 &prevOSIdleTicks, uint64 &prevProcTicks) {
	#if defined	WINDOWS
		return GetProcAndOSCPUUsage(0, procPercentUse, osPercentUse, prevOSTicks, prevOSIdleTicks, prevProcTicks);
	#else
		return GetProcAndOSCPUUsage(getpid(), procPercentUse, osPercentUse, prevOSTicks, prevOSIdleTicks, prevProcTicks);
	#endif
	return true;
}

bool GetProcAndOSCPUUsage(uint32 osProcID, double& procPercentUse, double& osPercentUse, uint64 &prevOSTicks, uint64 &prevOSIdleTicks, uint64 &prevProcTicks) {
	procPercentUse = osPercentUse = 0;

	uint64 totalTicks, kernelTicks, userTicks, idleTicks, procTotalTicks, procUserTicks, procKernelTicks;

	#if defined	WINDOWS
		FILETIME idleTime, kernelTime, userTime;
		if (!GetSystemTimes(&idleTime, &kernelTime, &userTime))
			return false;

		idleTicks = FileTimeToUint64(idleTime);
		kernelTicks = FileTimeToUint64(kernelTime);
		userTicks = FileTimeToUint64(userTime);
		totalTicks = kernelTicks + userTicks;

		FILETIME procCreationTime, procExitTime, procKernelTime, procUserTime;

		if (!osProcID) {
			if (!GetProcessTimes(GetCurrentProcess(), &procCreationTime, &procExitTime, &procKernelTime, &procUserTime))
				return false;
		}
		else {
			HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, osProcID);
			if (!hProcess)
				return false;

			if (!GetProcessTimes(hProcess, &procCreationTime, &procExitTime, &procKernelTime, &procUserTime))
				return false;
		}

		procKernelTicks = FileTimeToUint64(procKernelTime);
		procUserTicks = FileTimeToUint64(procUserTime);
		procTotalTicks = procKernelTicks + procUserTicks;

		// printf("Proc User: %llu  Kernel: %llu\n", procUserTicks, procKernelTicks);

	#elif defined OSX
		host_cpu_load_info_data_t cpuInfo;
		mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
		if (host_statistics64(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info64_t)&cpuInfo, &count) != KERN_SUCCESS)
			return false;
		// cpu_ticks are in Mach clock ticks (typically 100/s via CLK_TCK)
		// Convert to microseconds to match procTotalTicks units
		long ticksPerSec = sysconf(_SC_CLK_TCK);
		uint64 usPerTick = (ticksPerSec > 0) ? (1000000 / ticksPerSec) : 10000;
		totalTicks = ((uint64)cpuInfo.cpu_ticks[CPU_STATE_USER] + cpuInfo.cpu_ticks[CPU_STATE_SYSTEM] + cpuInfo.cpu_ticks[CPU_STATE_IDLE] + cpuInfo.cpu_ticks[CPU_STATE_NICE]) * usPerTick;
		idleTicks = (uint64)cpuInfo.cpu_ticks[CPU_STATE_IDLE] * usPerTick;
		if (osProcID == 0 || osProcID == (uint32)getpid()) {
			task_thread_times_info_data_t threadTimes;
			count = TASK_THREAD_TIMES_INFO_COUNT;
			if (task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t)&threadTimes, &count) == KERN_SUCCESS)
				procTotalTicks = (uint64)threadTimes.user_time.seconds * 1000000 + threadTimes.user_time.microseconds + (uint64)threadTimes.system_time.seconds * 1000000 + threadTimes.system_time.microseconds;
			else
				procTotalTicks = 0;
		} else {
			return false;
		}

	#else
		std::string statLine, procStatLine;
		std::ifstream statFile ("/proc/stat");
		std::ifstream procStatFile (StringFormat("/proc/%d/stat", osProcID).c_str());
		if (statFile.is_open())
			getline (statFile, statLine);
		else
			return false;

		if (procStatFile.is_open())
			getline (procStatFile, procStatLine);
		else
			return false;

		if (!statLine.length() || !procStatLine.length())
			return false;

		//cpu  56344 20038 65629 5095979 55549 9083 10717 0 0 0
		uint64 fields[10];
		int retval = sscanf(statLine.c_str(), "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
							&fields[0], &fields[1], &fields[2], &fields[3], &fields[4],
							&fields[5], &fields[6], &fields[7], &fields[8], &fields[9]);
		totalTicks = 0;
		for (int i=0; i<10; i++)
			totalTicks += fields[i];
		idleTicks = fields[3];

		uint64 utime_ticks, stime_ticks, cutime_ticks, cstime_ticks;
		retval = sscanf(procStatLine.c_str(), "%*d %*s %*c %*d %*d %*d %*d %*d %*u %*u %*u %*u %*u %llu %llu %llu %llu",
                &utime_ticks, &stime_ticks, &cutime_ticks, &cstime_ticks);

		procTotalTicks = utime_ticks + stime_ticks + cutime_ticks + cstime_ticks;
	#endif

	uint64 totalTicksSinceLastTime = totalTicks - prevOSTicks;
	uint64 idleTicksSinceLastTime  = idleTicks - prevOSIdleTicks;
	uint64 procTicksSinceLastTime  = procTotalTicks - prevProcTicks;

	osPercentUse = 1.0f-((totalTicksSinceLastTime > 0) ? ((double)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0);
	procPercentUse = ((totalTicksSinceLastTime > 0) ? ((double)procTicksSinceLastTime)/totalTicksSinceLastTime : 0);

//	printf("Idle: %llu   Total: %llu      %.2f%%\n",
//		idleTicksSinceLastTime, totalTicksSinceLastTime, percentUse*100);

	prevOSTicks = totalTicks;
	prevOSIdleTicks  = idleTicks;
	prevProcTicks = procTotalTicks;

	return true;
}

bool GetOSCPUUsage(double& percentUse) {
	percentUse = 0;

	static uint64 PreviousTotalTicks = 0;
	static uint64 PreviousIdleTicks = 0;

	uint64 totalTicks, kernelTicks, userTicks, idleTicks;

	#if defined	WINDOWS

		FILETIME idleTime, kernelTime, userTime;
		if (!GetSystemTimes(&idleTime, &kernelTime, &userTime))
			return false;

		idleTicks = FileTimeToUint64(idleTime);
		kernelTicks = FileTimeToUint64(kernelTime);
		userTicks = FileTimeToUint64(userTime);
		totalTicks = kernelTicks + userTicks;

	#elif defined OSX
		host_cpu_load_info_data_t cpuInfo;
		mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
		if (host_statistics64(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info64_t)&cpuInfo, &count) != KERN_SUCCESS)
			return false;
		totalTicks = (uint64)cpuInfo.cpu_ticks[CPU_STATE_USER] + cpuInfo.cpu_ticks[CPU_STATE_SYSTEM] + cpuInfo.cpu_ticks[CPU_STATE_IDLE] + cpuInfo.cpu_ticks[CPU_STATE_NICE];
		idleTicks = (uint64)cpuInfo.cpu_ticks[CPU_STATE_IDLE];
	#else
		std::string statLine;
		std::ifstream statFile("/proc/stat");
		if (!statFile.is_open() || !getline(statFile, statLine) || !statLine.length())
			return false;

		uint64 fields[10];
		if (sscanf(statLine.c_str(), "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu",
							&fields[0], &fields[1], &fields[2], &fields[3], &fields[4],
							&fields[5], &fields[6], &fields[7], &fields[8], &fields[9]) < 10)
			return false;
		totalTicks = 0;
		for (int i = 0; i < 10; i++)
			totalTicks += fields[i];
		idleTicks = fields[3];
	#endif

	uint64 totalTicksSinceLastTime = totalTicks - PreviousTotalTicks;
	uint64 idleTicksSinceLastTime  = idleTicks - PreviousIdleTicks;

	percentUse = 1.0f-((totalTicksSinceLastTime > 0) ? ((double)idleTicksSinceLastTime)/totalTicksSinceLastTime : 0);

//	printf("Idle: %llu   Total: %llu      %.2f%%\n",
//		idleTicksSinceLastTime, totalTicksSinceLastTime, percentUse*100);

	PreviousTotalTicks = totalTicks;
	PreviousIdleTicks  = idleTicks;

	return true;
}

bool GetThreadPriority(ThreadHandle hThread, uint16 priority) {
	
	#if defined	WINDOWS
		uint16 prio = ::GetThreadPriority(hThread);
		priority = FromOSPriority(prio);
	#else
		struct sched_param sched;
		int policy;
		#ifdef __APPLE__
			policy = SCHED_OTHER;
		#else
			policy = sched_getscheduler(0);
			if(policy < 0)
				return false;
		#endif
		if (pthread_getschedparam(hThread, &policy, &sched) != 0)
			return false;

		priority = FromOSPriority(sched.sched_priority);
	#endif
	return true;
}

bool SetThreadPriority(ThreadHandle hThread, uint16 priority) {
	#if defined	WINDOWS
		return (::SetThreadPriority(hThread, ToOSPriority(priority)) != 0);
	#else
		int policy;
		struct sched_param sched;
		memset(&sched, 0, sizeof(struct sched_param));

		policy = SCHED_FIFO;
		// policy = SCHED_RR;
		sched.sched_priority = ToOSPriority(priority);

		int res = pthread_setschedparam(hThread, policy, &sched);
		if (res != 0)
			return false;
	#endif
	return true;
}

int ToOSPriority(int pri) {
	int val;

	#if defined	WINDOWS
		if (pri == 0)
			val = THREAD_PRIORITY_NORMAL;
		else if (pri >= 90)
			val = THREAD_PRIORITY_TIME_CRITICAL;
		else if (pri >= 80)
			val = THREAD_PRIORITY_HIGHEST;
		else if (pri >= 70)
			val = THREAD_PRIORITY_ABOVE_NORMAL;
		else if (pri >= 60)
			val = THREAD_PRIORITY_NORMAL;
		else if (pri >= 50)
			val = THREAD_PRIORITY_BELOW_NORMAL;
		else if (pri >= 40)
			val = THREAD_PRIORITY_LOWEST;
		else
			val = THREAD_PRIORITY_IDLE;
	#else
		if (pri == 0)
			val = 40;
		else if (pri >= 90)
			val = 60;
		else if (pri >= 80)
			val = 55;
		else if (pri >= 70)
			val = 40;
		else if (pri >= 60)
			val = 35;
		else if (pri >= 50)
			val = 30;
		else if (pri >= 40)
			val = 25;
		else if (pri >= 30)
			val = 20;
		else if (pri >= 20)
			val = 15;
		else if (pri >= 10)
			val = 10;
		else
			val = 5;
	#endif

	return val;
}

int FromOSPriority(int pri) {

	#if defined	WINDOWS
	switch (pri) {
		case THREAD_PRIORITY_TIME_CRITICAL:
			return 80;
		case THREAD_PRIORITY_HIGHEST:
			return 60;
		case THREAD_PRIORITY_ABOVE_NORMAL:
			return 55;
		case THREAD_PRIORITY_NORMAL:
			return 50;
		case THREAD_PRIORITY_BELOW_NORMAL:
			return 40;
		case THREAD_PRIORITY_LOWEST:
			return 30;
		default:
			return 20;
	}
	#else
		return pri;
	#endif
}

bool SignalThread(ThreadHandle hThread, int32 signal) {
	#if defined	WINDOWS
		// ######################################
	#else
		// ######################################
	#endif
	return false;
}




//////////////////////////////////////////////////////////////
//                       Desktop                            //
//////////////////////////////////////////////////////////////

bool GetDesktopSize(uint32& width, uint32& height) {
	width = height = 0;
	#if defined	WINDOWS
		RECT desktop;
		// Get a handle to the desktop window
		const HWND hDesktop = GetDesktopWindow();
		// Get the size of screen to the variable desktop
		if (!GetWindowRect(hDesktop, &desktop))
			return false;
		// The top left corner will have coordinates (0,0)
		// and the bottom right corner will have coordinates
		// (horizontal, vertical)
		width = (uint32)desktop.right;
		height = (uint32)desktop.bottom;
		return true;
	#else
		return false;
	#endif
}

bool RenameConsoleWindow(const char* name, bool prepend) {
	#if defined	WINDOWS
		if (prepend) {
			uint32 size;
			char* oldName = new char[1024];
			if (!GetConsoleTitle(oldName, 1024)) {
				delete [] oldName;
				return false;
			}
			char* newName = StringFormat(size, "%s - %s", name, oldName);
			bool res = (SetConsoleTitle(name) != 0);
			delete [] newName;
			delete [] oldName;
			return res;
		}
		else
			return (SetConsoleTitle(name) != 0);
	#else
		return false;
	#endif
}

bool MoveConsoleWindow(int32 x, int32 y, int32 w, int32 h) {
	#if defined	WINDOWS
		char* consoleName = new char[1024];
		if (!GetConsoleTitle(consoleName, 1024)) {
			delete [] consoleName;
			return false;
		}
		HWND handle = FindWindow(NULL, consoleName);
		if (!handle) {
			delete [] consoleName;
			return false;
		}
		RECT wRect;
		if (!GetWindowRect(handle, &wRect)) {
			delete [] consoleName;
			return false;
		}

		int conX = (x<0) ? wRect.left : x;
		int conY = (y<0) ? wRect.top : y;
		int conW = (w<0) ? wRect.right - wRect.left : w;
		int conH = (h<0) ? wRect.bottom - wRect.top : h;

		bool res = (MoveWindow(handle, conX, conY, conW, conH, true) != 0);
		delete [] consoleName;
		return res;

	#else
		return false;
	#endif
}


//////////////////////////////////////////////////////////////
//                      Processes                           //
//////////////////////////////////////////////////////////////

uint32 GetLocalProcessID() {
	#if defined	WINDOWS
		return (uint32)GetCurrentProcessId();
	#else
		return (uint32)getpid();
	#endif
}


int RunOSCommand(const char* cmdline, const char* initdir, uint32 timeout, std::string& stdoutString, std::string& stderrString) {
	if (!cmdline || !strlen(cmdline))
		return -1;

	#if defined	WINDOWS
		HANDLE g_hChildStd_OUT_Rd = NULL;
		HANDLE g_hChildStd_OUT_Wr = NULL;
		HANDLE g_hChildStd_ERR_Rd = NULL;
		HANDLE g_hChildStd_ERR_Wr = NULL;

		SECURITY_ATTRIBUTES sa; 
		// Set the bInheritHandle flag so pipe handles are inherited. 
		sa.nLength = sizeof(SECURITY_ATTRIBUTES); 
		sa.bInheritHandle = TRUE; 
		sa.lpSecurityDescriptor = NULL; 
		// Create a pipe for the child process's STDERR. 
		if ( ! CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &sa, 0) ) {
			return -1;
		}
		// Ensure the read handle to the pipe for STDERR is not inherited.
		if ( ! SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0) ){
			return -1;
		}
		// Create a pipe for the child process's STDOUT. 
		if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0) ) {
			return -1;
		}
		// Ensure the read handle to the pipe for STDOUT is not inherited
		if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) ){
			return -1;
		}
		PROCESS_INFORMATION piProcInfo; 
		STARTUPINFO siStartInfo;
		bool bSuccess = FALSE; 

		// Set up members of the PROCESS_INFORMATION structure. 
		ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

		// Set up members of the STARTUPINFO structure. 
		// This structure specifies the STDERR and STDOUT handles for redirection.
		ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
		siStartInfo.cb = sizeof(STARTUPINFO); 
		siStartInfo.hStdError = g_hChildStd_ERR_Wr;
		siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
		siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

		uint32 size = 0;
		char* cmd = utils::StringFormat(size, cmdline);

		// Create the child process. 
		bSuccess = (CreateProcess(NULL,
			cmd,     // command line 
			NULL,          // process security attributes 
			NULL,          // primary thread security attributes 
			TRUE,          // handles are inherited 
			0,             // creation flags 
			NULL,          // use parent's environment 
			initdir,          // use parent's current directory 
			&siStartInfo,  // STARTUPINFO pointer 
			&piProcInfo) != 0);  // receives PROCESS_INFORMATION
		CloseHandle(g_hChildStd_ERR_Wr);
		CloseHandle(g_hChildStd_OUT_Wr);
		// If an error occurs, exit the application. 
		if ( ! bSuccess ) {
			return -1;
		}
	
		uint64 startTime = GetTimeNow();

		DWORD dwRead; 
		char* chBuf = new char[PROCBUFSIZE];
		bSuccess = FALSE;
		for (;;) { 
			bSuccess=(ReadFile( g_hChildStd_OUT_Rd, chBuf, PROCBUFSIZE, &dwRead, NULL) != 0);
			if( ! bSuccess || dwRead == 0 ) break; 
			std::string s(chBuf, dwRead);
			stdoutString += s;
			if(GetTimeAgeMS(startTime) > (int32)timeout) break; 
		} 
		dwRead = 0;
		for (;;) { 
			bSuccess=(ReadFile( g_hChildStd_ERR_Rd, chBuf, PROCBUFSIZE, &dwRead, NULL) != 0);
			if( ! bSuccess || dwRead == 0 ) break; 
			std::string s(chBuf, dwRead);
			stderrString += s;
			if(GetTimeAgeMS(startTime) > (int32)timeout) break; 
		}
		delete [] chBuf;

		DWORD exitcode;
		if (GetExitCodeProcess(piProcInfo.hProcess, &exitcode) && (exitcode == STILL_ACTIVE)) {
			TerminateProcess(piProcInfo.hProcess, 1);
		}
		CloseHandle(g_hChildStd_ERR_Rd);
		CloseHandle(g_hChildStd_OUT_Rd);
		CloseHandle(piProcInfo.hProcess);
		CloseHandle(piProcInfo.hThread);
		return exitcode; 
	#else
		fflush(stdin);
		fflush(stdout);
		FILE* runfile = popen(cmdline, "r");
		if (runfile == NULL)
			return -1;

		int exitcode = -1;
		int status;
		int res;
		int size = 4096;
		char* buffer = new char[size+1];
		
		res = fread(buffer, 1, size, runfile);
		if (res <= 0) {
			status = pclose(runfile);
			exitcode = WEXITSTATUS(status);
			delete [] buffer;
			return exitcode;
		}
		do {
			buffer[res] = 0;
			stdoutString += buffer;
		} while (res = fread(buffer, 1, size, runfile));

		status = pclose(runfile);
		exitcode = WEXITSTATUS(status);
		delete [] buffer;
		return exitcode;

	//
	//int size = 1024;
	//	char name[1024];
	//	if (!RunOSTextCommand("uname -n", name, size))
	//		return "";
	//	
	//
	//int argc = 0;
	//	char **argv=SplitCommandline(cmdline, argc);
	//	if (!argc || !argv)
	//		return -1;

	//	/* since pipes are unidirectional, we need two pipes.
	//	   one for data to flow from parent's stdout to child's
	//	   stdin and the other for child's stdout to flow to
	//	   parent's stdin */

	//	int* pipes = new int[NUM_PIPES][2];
	//	int outfd[2];
	//	int infd[2];

	//	// pipes for parent to write and read
	//	pipe((*pipes)[PARENT_READ_PIPE]);
	//	pipe((*pipes)[PARENT_WRITE_PIPE]);

	//	if(!fork()) {

	//		dup2(CHILD_READ_FD, STDIN_FILENO);
	//		dup2(CHILD_WRITE_FD, STDOUT_FILENO);

	//		/* Close fds not required by child. Also, we don't
	//			want the exec'ed program to know these existed */
	//		close(CHILD_READ_FD);
	//		close(CHILD_WRITE_FD);
	//		close(PARENT_READ_FD);
	//		close(PARENT_WRITE_FD);

	//		execv(argv[0], argv);
	//	} else {
	//		char buffer[100];
	//		int count;

	//		/* close fds not required by parent */       
	//		close(CHILD_READ_FD);
	//		close(CHILD_WRITE_FD);

	//		// Write to child�s stdin
	//		//write(PARENT_WRITE_FD, "2^32\n", 5);

	//		// Read from child�s stdout
	//		count = read(PARENT_READ_FD, buffer, sizeof(buffer)-1);
	//		if (count >= 0) {
	//			buffer[count] = 0;
	//			printf("%s", buffer);
	//		} else {
	//			printf("IO Error\n");
	//		}
	//		// ################
	//		// Not yet complete!!!
	//	}
	#endif

}


uint32 NewProcess(const char* cmdline, const char* initdir, const char* title, int16 x, int16 y, int16 w, int16 h) {

	if (!cmdline || !strlen(cmdline))
		return 0;

	#if defined	WINDOWS

		if (!ProcessInformationMap) {
			ProcessInformationMap = new std::map<uint32, ProcessData*>;
			ghJob = CreateJobObject(NULL, NULL); // GLOBAL
			if (ghJob) {
				JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
				// Configure all child processes associated with the job to terminate when the
				jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
				SetInformationJobObject(ghJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));
			}
		}

		ProcessData* pData = new ProcessData;

		ZeroMemory(&(pData->procInfo), sizeof(pData->procInfo));
		ZeroMemory(&(pData->si), sizeof(pData->si));
		pData->si.cb = sizeof(pData->si);
		pData->si.wShowWindow = SW_SHOWNOACTIVATE; //SW_SHOW; // SW_HIDE;

		if ( (x>=0) && (y>=0) && (w>=0) && (h>=0) ) {
			pData->si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USEPOSITION | STARTF_USESIZE;
			pData->si.dwX = x;
			pData->si.dwY = y;
			pData->si.dwXSize = w;
			pData->si.dwYSize = h;
		}
		else {
			pData->si.dwFlags = STARTF_USESHOWWINDOW;
		}

		uint32 size = 0;
		char* titleCopy = NULL;
		if (title) {
			titleCopy = utils::StringFormat(size, title);
			pData->si.lpTitle = titleCopy;
		}

		char* cmd = utils::StringFormat(size, cmdline);
		//utils::strcpyavail(cmd, cmdline, (uint32)strlen(cmdline)+1, true);

		// DETACHED_PROCESS 
		int res = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, initdir, &pData->si, &pData->procInfo);
		delete [] titleCopy;
		delete [] cmd;
		if (res == 0) {
			char* msg = new char[2048];
			int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, 
					   NULL, GetLastError(), 0, msg, sizeof(msg), NULL);
			msg[length] = '\0';
			LogPrint(0, LOG_SYSTEM, 0, "Error creating new process: %s\n%s", msg, cmd);
			delete [] msg;
			delete pData;
			return 0;
		}

		if (ghJob)
			AssignProcessToJobObject(ghJob, pData->procInfo.hProcess);

		ProcessInformationMap->insert(Proc_Pair((uint32)pData->procInfo.dwProcessId, pData));
		return pData->procInfo.dwProcessId;
		
	#else

		//std::vector<std::string> args = utils::TextListSplit(cmdline, " ");
		//int argc = args.size();
		//// const char** argv = new const char*[argc+1];
		//char** argv = new char*[argc+1];
		//int len;
		//for (int n=0; n<argc; n++) {
		//	argv[n] = new char[args[n].size() + 1];
		//	utils::strcpyavail(argv[n], args[n].c_str(), args[n].size()+1, true);
		//}
		//argv[argc] = NULL;

		int argc = 0;
		char** argv = SplitCommandline(cmdline, argc);
		if (!argc)
			return -1;

		pid_t ppid_before_fork = getpid();
		int pid = fork();

		if (pid < 0) {
			DeleteCommandline(argv, argc);
			return 0;
		}
		// We are the child process
		else if (pid == 0) {

			if ( initdir && (chdir(initdir) != 0) ) {
				LogPrint(0,LOG_SYSTEM,0,"Could not run process '%s' from starup dir '%s'\n", cmdline, initdir);
				exit(0);
			}

			#ifndef __APPLE__
			int r = prctl(PR_SET_PDEATHSIG, SIGTERM);
			if (r == -1) { perror(0); exit(1); }
			// test in case the original parent exited just
			// before the prctl() call
			if (getppid() != ppid_before_fork)
				exit(1);
			#endif

			execvp(argv[0],argv);
			// here we exit the program, if we continue the call failed...

			int res = errno;
			//  If program didn't take over the exec call failed.
			LogPrint(0,LOG_SYSTEM,0,"Could not create new process: %s (%d)!\n", cmdline, res);
			exit(0);

		}
		// we are in the calling process
		else {
			DeleteCommandline(argv, argc);
			return pid;
		}

	#endif
}

uint8 GetProcessStatus(uint32 proc, int &returncode) {
	returncode = 0;
	#if defined	WINDOWS
		DWORD exitcode;
		std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
		if (i != ProcessInformationMap->end()) {
			ProcessData* pData = i->second;
			if (GetExitCodeProcess(pData->procInfo.hProcess, &exitcode) && (exitcode == STILL_ACTIVE)) {
				return PROC_RUNNING;
			}
			else {
				ProcessInformationMap->erase(i);
				CloseHandle(pData->procInfo.hProcess);
				CloseHandle(pData->procInfo.hThread);
				if (pData->hOutRead) CloseHandle(pData->hOutRead);
				if (pData->hErrRead) CloseHandle(pData->hErrRead);
				delete pData;
				returncode = exitcode;
				return PROC_TERMINATED;
			}
		}
		else {
			// We didn't create this process, look it up by id
			HANDLE hproc = OpenProcess(NULL, false, (DWORD)proc);
			if (!hproc)
				return PROC_ERROR;
			if (GetExitCodeProcess(hproc, &exitcode) && (exitcode == STILL_ACTIVE)) {
				CloseHandle(hproc);
				return PROC_RUNNING;
			}
			else {
				CloseHandle(hproc);
				return PROC_TERMINATED;
			}
		}
	#else
		int status;
		int res = waitpid(proc, &status, WNOHANG);

		if (res == proc) {
		  //if (WIFEXITED(status)) {
			 //  printf("exited, status=%d\n", WEXITSTATUS(status));
		  // } else if (WIFSIGNALED(status)) {
			 //  printf("killed by signal %d\n", WTERMSIG(status));
		  // } else if (WIFSTOPPED(status)) {
			 //  printf("stopped by signal %d\n", WSTOPSIG(status));
		  // } else if (WIFCONTINUED(status)) {
			 //  printf("continued\n");
		  // }
			returncode = WEXITSTATUS(status);
			//printf("Return code: %d\n", returncode);
			return PROC_TERMINATED;
		}
		else if (res == 0) {
			return PROC_RUNNING;
		}
		return PROC_TERMINATED;
	#endif
}

bool EndProcess(uint32 proc) {
	#if defined	WINDOWS
		if (!ProcessInformationMap)
			return true;
		std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
		if (i != ProcessInformationMap->end()) {
			ProcessData* pData = i->second;
			TerminateProcess(pData->procInfo.hProcess, 1);
			CloseHandle(pData->procInfo.hProcess);
			CloseHandle(pData->procInfo.hThread);
			if (pData->hOutRead) CloseHandle(pData->hOutRead);
			if (pData->hErrRead) CloseHandle(pData->hErrRead);
			ProcessInformationMap->erase(i);
			delete pData;
			return true;
		}
		else {
			HANDLE hproc = OpenProcess(NULL, NULL, (DWORD)proc);
			if (!hproc)
				return true;
			if (TerminateProcess(hproc, 1) == 0) {
				CloseHandle(hproc);
				return false;
			}
			else {
				CloseHandle(hproc);
				return true;
			}
		}
	#else
		return (kill(proc, SIGKILL) == 0);
	#endif
}

uint32 NewProcessEx(const char* cmdline, const char* initdir, const char* title,
					const std::map<std::string, std::string>* env,
					bool captureOutput, bool ignoreOutput, bool newProcessGroup) {
	if (!cmdline || !strlen(cmdline))
		return 0;

	#if defined	WINDOWS

		if (!ProcessInformationMap) {
			ProcessInformationMap = new std::map<uint32, ProcessData*>;
			ghJob = CreateJobObject(NULL, NULL); // GLOBAL
			if (ghJob) {
				JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
				jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
				SetInformationJobObject(ghJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli));
			}
		}

		ProcessData* pData = new ProcessData;
		ZeroMemory(&(pData->procInfo), sizeof(pData->procInfo));
		ZeroMemory(&(pData->si), sizeof(pData->si));
		pData->si.cb = sizeof(pData->si);
		pData->si.wShowWindow = SW_SHOWNOACTIVATE;
		pData->si.dwFlags = STARTF_USESHOWWINDOW;

		HANDLE hOutWrite = NULL, hErrWrite = NULL, hNull = NULL;
		SECURITY_ATTRIBUTES sa;
		ZeroMemory(&sa, sizeof(sa));
		sa.nLength = sizeof(sa);
		sa.bInheritHandle = TRUE;
		sa.lpSecurityDescriptor = NULL;

		if (captureOutput) {
			// Create stdout/stderr pipes; make only the WRITE ends inheritable.
			if ( !CreatePipe(&pData->hOutRead, &hOutWrite, &sa, 0) ||
				 !CreatePipe(&pData->hErrRead, &hErrWrite, &sa, 0) ) {
				LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: could not create output pipes for '%s'", cmdline);
				if (pData->hOutRead) CloseHandle(pData->hOutRead);
				if (hOutWrite) CloseHandle(hOutWrite);
				if (pData->hErrRead) CloseHandle(pData->hErrRead);
				delete pData;
				return 0;
			}
			SetHandleInformation(pData->hOutRead, HANDLE_FLAG_INHERIT, 0);
			SetHandleInformation(pData->hErrRead, HANDLE_FLAG_INHERIT, 0);
			pData->si.dwFlags |= STARTF_USESTDHANDLES;
			pData->si.hStdOutput = hOutWrite;
			pData->si.hStdError = hErrWrite;
			pData->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
		}
		else if (ignoreOutput) {
			hNull = CreateFile("NUL", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
							   &sa, OPEN_EXISTING, 0, NULL);
			if (hNull != INVALID_HANDLE_VALUE) {
				pData->si.dwFlags |= STARTF_USESTDHANDLES;
				pData->si.hStdOutput = hNull;
				pData->si.hStdError = hNull;
				pData->si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
			}
			else hNull = NULL;
		}

		// Build the environment block (parent env + overrides) if any overrides given.
		char* envBlock = NULL;
		if (env && env->size()) {
			std::map<std::string, std::string> merged;
			LPCH parentEnv = GetEnvironmentStrings();
			if (parentEnv) {
				for (LPCH p = parentEnv; *p; ) {
					std::string entry = p;
					size_t eq = entry.find('=');
					if (eq != std::string::npos && eq > 0)
						merged[entry.substr(0, eq)] = entry.substr(eq + 1);
					p += entry.size() + 1;
				}
				FreeEnvironmentStrings(parentEnv);
			}
			std::map<std::string, std::string>::const_iterator ei = env->begin();
			while (ei != env->end()) { merged[ei->first] = ei->second; ei++; }
			size_t total = 1;
			std::map<std::string, std::string>::iterator mi = merged.begin();
			while (mi != merged.end()) { total += mi->first.size() + 1 + mi->second.size() + 1; mi++; }
			envBlock = new char[total];
			size_t off = 0;
			for (mi = merged.begin(); mi != merged.end(); mi++) {
				std::string kv = mi->first + "=" + mi->second;
				memcpy(envBlock + off, kv.c_str(), kv.size() + 1);
				off += kv.size() + 1;
			}
			envBlock[off] = 0;
		}

		DWORD flags = CREATE_NEW_CONSOLE;
		if (newProcessGroup)
			flags |= CREATE_NEW_PROCESS_GROUP;

		uint32 size = 0;
		char* titleCopy = NULL;
		if (title) { titleCopy = utils::StringFormat(size, title); pData->si.lpTitle = titleCopy; }
		char* cmd = utils::StringFormat(size, cmdline);

		int res = CreateProcess(NULL, cmd, NULL, NULL, TRUE, flags, envBlock, initdir, &pData->si, &pData->procInfo);

		delete [] titleCopy;
		delete [] cmd;
		if (envBlock) delete [] envBlock;
		// Parent closes the child's write ends and the null handle; keeps the read ends.
		if (hOutWrite) CloseHandle(hOutWrite);
		if (hErrWrite) CloseHandle(hErrWrite);
		if (hNull) CloseHandle(hNull);

		if (res == 0) {
			char msg[2048];
			int length = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), 0, msg, sizeof(msg), NULL);
			msg[length] = '\0';
			LogPrint(0, LOG_SYSTEM, 0, "NewProcessEx: error creating process: %s\n%s", msg, cmdline);
			if (pData->hOutRead) CloseHandle(pData->hOutRead);
			if (pData->hErrRead) CloseHandle(pData->hErrRead);
			delete pData;
			return 0;
		}
		if (ghJob)
			AssignProcessToJobObject(ghJob, pData->procInfo.hProcess);
		ProcessInformationMap->insert(Proc_Pair((uint32)pData->procInfo.dwProcessId, pData));
		return pData->procInfo.dwProcessId;

	#else
		// POSIX: env/workdir/capture are handled by the caller (ExternalExecManager
		// forks directly so it can set up pipes and per-child env in the child).
		// This wrapper is provided for API symmetry; it ignores capture/env here.
		(void)env; (void)captureOutput; (void)ignoreOutput; (void)newProcessGroup;
		return NewProcess(cmdline, initdir, title);
	#endif
}

bool ReadProcessOutput(uint32 proc, std::string& out, std::string& err) {
	#if defined	WINDOWS
		if (!ProcessInformationMap) return false;
		std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
		if (i == ProcessInformationMap->end()) return false;
		ProcessData* pData = i->second;
		HANDLE handles[2] = { pData->hOutRead, pData->hErrRead };
		std::string* targets[2] = { &out, &err };
		char buf[4096];
		for (int n = 0; n < 2; n++) {
			if (!handles[n]) continue;
			DWORD avail = 0;
			// PeekNamedPipe works on anonymous pipes to avoid blocking on ReadFile.
			while (PeekNamedPipe(handles[n], NULL, 0, NULL, &avail, NULL) && avail) {
				DWORD toRead = avail < sizeof(buf) ? avail : (DWORD)sizeof(buf);
				DWORD got = 0;
				if (!ReadFile(handles[n], buf, toRead, &got, NULL) || !got) break;
				targets[n]->append(buf, got);
			}
		}
		return true;
	#else
		(void)proc; (void)out; (void)err;
		return false;
	#endif
}

bool SendProcessBreak(uint32 proc, int sig) {
	#if defined	WINDOWS
		(void)sig;
		// Requires the child to have been created with CREATE_NEW_PROCESS_GROUP.
		// The group id equals the child's process id.
		return (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, (DWORD)proc) != 0);
	#else
		return (kill((pid_t)proc, sig) == 0);
	#endif
}

uint8 WaitForProcess(uint32 proc, uint32 timeout, int &returncode) {
	#if defined	WINDOWS
		DWORD exitcode;
		std::map<uint32, ProcessData*>::iterator i = ProcessInformationMap->find(proc);
		if (i != ProcessInformationMap->end()) {
			ProcessData* pData = i->second;
			if (WaitForSingleObject(pData->procInfo.hProcess, timeout) == WAIT_OBJECT_0) {
				if (GetExitCodeProcess(pData->procInfo.hProcess, &exitcode) && (exitcode == STILL_ACTIVE)) {
					return PROC_RUNNING;
				}
				else {
				ProcessInformationMap->erase(i);
				CloseHandle(pData->procInfo.hProcess);
				CloseHandle(pData->procInfo.hThread);
				if (pData->hOutRead) CloseHandle(pData->hOutRead);
				if (pData->hErrRead) CloseHandle(pData->hErrRead);
				delete pData;
				returncode = exitcode;
				return PROC_TERMINATED;
				}
			}
			return PROC_TIMEOUT;
		}
		else {
			HANDLE hproc = OpenProcess(NULL, NULL, (DWORD)proc);
			if (!hproc) {
				return PROC_ERROR;
			}
			if (WaitForSingleObject(hproc, timeout) == WAIT_OBJECT_0) {
				if (GetExitCodeProcess(hproc, &exitcode) && (exitcode == STILL_ACTIVE)) {
					CloseHandle(hproc);
					return PROC_RUNNING;
				}
				else {
					returncode = exitcode;
					CloseHandle(hproc);
					return PROC_TERMINATED;
				}
			}
			else {
				CloseHandle(hproc);
				return PROC_RUNNING;
			}
		}
	#else
		uint64 start = GetTimeNow();	
		do {
			if (GetProcessStatus(proc, returncode) < PROC_RUNNING)
				return PROC_TERMINATED;
			utils::Sleep(10);
		} while (GetTimeAgeMS(start) < timeout);
		return PROC_RUNNING;

	#endif
}









//////////////////////////////////////////////////////////////
//                      DLL Libraries                       //
//////////////////////////////////////////////////////////////

bool SetCommandLine(int argc, char* argv[]) {

	if (!CommandLineInfo::CommandLineInfoSingleton)
		CommandLineInfo::CommandLineInfoSingleton = new CommandLineInfo();

	std::string key;
	const char* val;
	int32 n;
	CommandLineInfo::CommandLineInfoSingleton->CommandLineString = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec = argv[0];
	CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.push_back(argv[0]);
	for (n=1; n<argc; n++) {
		CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.push_back(argv[n]);
		key = argv[n];
		val = strchr(argv[n], '=');
		if (val)
			CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs[key.substr(0, val-argv[n])] = val+1;
		CommandLineInfo::CommandLineInfoSingleton->CommandLineString += ' ';
		CommandLineInfo::CommandLineInfoSingleton->CommandLineString += argv[n];
	}

	//printf("Full: '%s'\n", CommandLineString.c_str());
	//printf("Base: '%s'\n", CommandLineExec.c_str());

	//n = 0;
	//std::vector<std::string>::iterator it = CommandLineItems.begin(), itEnd = CommandLineItems.end();
	//while (it != itEnd) {
	//	printf("[%u] '%s'\n", n++, (*it).c_str());	
	//	it++;
	//}

	//n = 0;
	//std::map<std::string, std::string>::iterator mit = CommandLineArgs.begin(), mitEnd = CommandLineArgs.end();
	//while (mit != mitEnd) {
	//	printf("[%u] '%s' = '%s'\n", n++, mit->first.c_str(), mit->second.c_str());	
	//	mit++;
	//}

	//printf("Executable: '%s' '%s'\n", GetCommandLinePath().c_str(), GetCommandLineExecutableOnly().c_str());
	return true;
}

std::string GetCommandLine() {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	return CommandLineInfo::CommandLineInfoSingleton->CommandLineString;
}

std::string GetCommandLinePath() {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	std::string::size_type p1 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('/');
	std::string::size_type p2 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('\\');
	if (p1 == std::string::npos) {
		if (p2 == std::string::npos)
			return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
		else
			return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, p2+1);
	}
	else if (p2 == std::string::npos)
		return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, p1+1);
	else
		return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(0, (p1 > p2) ? p1+1 : p2+1);
}

std::string GetCommandLineExecutable() {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
}

std::string GetCommandLineExecutableOnly() {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	std::string::size_type p1 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('/');
	std::string::size_type p2 = CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.find_last_of('\\');
	if (p1 == std::string::npos) {
		if (p2 == std::string::npos)
			return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec;
		else
			return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(p2+1);
	}
	else if (p2 == std::string::npos)
		return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr(p1+1);
	else
		return CommandLineInfo::CommandLineInfoSingleton->CommandLineExec.substr((p1 > p2) ? p1+1 : p2+1);
}

uint32 GetCommandLineArgCount() {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return 0;
	return (uint32)CommandLineInfo::CommandLineInfoSingleton->CommandLineItems.size();
}

std::string GetCommandLineArg(uint16 n) {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	return CommandLineInfo::CommandLineInfoSingleton->CommandLineItems[n];
}

std::string GetCommandLineArg(const char* key) {
	if (!CommandLineInfo::CommandLineInfoSingleton)
		return "";
	return CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs[key];
}

bool HasCommandLineArg(const char* key, std::string& value) {
	if (!CommandLineInfo::CommandLineInfoSingleton || !key)
		return false;
	std::map<std::string, std::string>::const_iterator it =
		CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs.find(key);
	if (it == CommandLineInfo::CommandLineInfoSingleton->CommandLineArgs.end())
		return false;
	value = it->second;
	return true;
}



Library::Library() {
	handle = NULL;
}
Library::~Library() {
	if (handle) {
		#ifdef WINDOWS
			FreeLibrary(handle);
		#else // WINDOWS
			// dlclose() bug: https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=28625
			// __dso_handle unreferenced
			// if there are any globally desclared objects in the library!!!
			dlclose(handle);
		#endif // WINDOWS
	}
}

std::string Library::patchLibraryFilename(const char* filename, const char* path) {
	#ifdef WINDOWS
		return filename;
	#else
		std::string realLibName;
		if (utils::TextEndsWith(filename, ".so"))
			realLibName = filename;
		else
			realLibName = utils::StringFormat("%s.so", filename);

		int32 filenameStart;
		if ( (filenameStart = realLibName.find_last_of('/') == std::string::npos ))
			filenameStart = 0;
		else
			filenameStart++;

		if (!utils::TextStartsWith(filename + filenameStart, "lib", true))
			realLibName.insert(filenameStart, "lib");

		if (!filenameStart) {
			if (!path)
				realLibName.insert(0, "./");
			else
				realLibName.insert(0, path);
		}

		return realLibName;
	#endif
}

bool Library::load(const char* filename) {
	if (!filename)
		return false;
	std::string errorText;
	//printf("Trying to find library: '%s'...\n", filename);

	#ifdef WINDOWS
		LPVOID lpMsgBuf; //Message Buffer
		#ifdef _DEBUG
			handle = LoadLibrary(utils::StringFormat("%sDebug", filename).c_str());
			if (handle == NULL) {
				FormatMessage(
					FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
					NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
					(LPTSTR)&lpMsgBuf, 0, NULL);
				LogPrint(0, LOG_SYSTEM, 0, "Could not find or load debug library '%sDebug': %s", filename, lpMsgBuf);
				LocalFree(lpMsgBuf);
				return false;
			}
		#else
			handle = LoadLibrary(filename);
			if (handle == NULL) {
				FormatMessage(
					FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
					NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
					(LPTSTR) &lpMsgBuf, 0, NULL);
				LogPrint(0, LOG_SYSTEM, 0, "Could not find or load library '%s': %s", filename, lpMsgBuf);
				LocalFree(lpMsgBuf);
				return false;
			}
		#endif
	#else // WINDOWS
		handle = 0;
		const char* dlErrorText;
		std::string realLibName = patchLibraryFilename(filename);
		//printf("Trying patched library: '%s'...\n", realLibName.c_str());
	
		// clear errors
		dlerror();
		#ifdef _DEBUG
			std::string debugName = realLibName;
			utils::StringSingleReplace(debugName, ".so", "Debug.so", true);
			// printf("Trying debug library: '%s'...\n", debugName.c_str());
			handle = dlopen(debugName.c_str(), RTLD_NOW | RTLD_GLOBAL);
			if (!handle) {
				dlErrorText = dlerror();
				// printf("Error 1: '%s'...\n", dlErrorText);
				if (dlErrorText && strlen(dlErrorText) &&
					!(strstr(dlErrorText, "No such file") && strstr(dlErrorText, debugName.c_str())))
					errorText = dlErrorText;
			}
		#endif

		if (!handle && !errorText.size()) {
			// handle = dlopen(realLibName.c_str(), RTLD_NOW | RTLD_GLOBAL);
			if (!handle) {
				dlErrorText = dlerror();
				// printf("Error 2: '%s'...\n", dlErrorText);
				if (dlErrorText && strlen(dlErrorText) &&
					!(strstr(dlErrorText, "No such file") && strstr(dlErrorText, realLibName.c_str())))
					errorText = dlErrorText;
			}
		}

		if (!handle && !errorText.size() && !strchr(filename, '/')) {
			realLibName = patchLibraryFilename(filename, utils::GetCommandLinePath().c_str());
			// printf("Trying library: '%s'...\n", realLibName.c_str());
			handle = dlopen(realLibName.c_str(), RTLD_NOW | RTLD_GLOBAL);
			if (!handle) {
				dlErrorText = dlerror();
				// printf("Error 3: '%s'...\n", dlErrorText);
				if (dlErrorText && strlen(dlErrorText) &&
					!(strstr(dlErrorText, "No such file") && strstr(dlErrorText, realLibName.c_str())))
					errorText = dlErrorText;
			}
		}
			
		if (handle == NULL) {
			if (errorText.size())
				LogPrint(0, LOG_SYSTEM, 0, "Could not load library file '%s': %s", realLibName.c_str(), errorText.c_str());
			else
				LogPrint(0, LOG_SYSTEM, 0, "Could not find library file '%s'", filename);
			return false;
		}
	#endif // WINDOWS
	return true;
}

LibraryFunction Library::getFunction(const char* funcName) {
	if (handle == NULL)
		return NULL;
	if (!strlen(funcName))
		return NULL;

	LibraryFunction func;

	#ifdef WINDOWS
		func = (LibraryFunction)GetProcAddress(handle, funcName);
		if (func == NULL) {
			LPVOID lpMsgBuf; //Message Buffer
			//Generate Error Message from GetLastError
			FormatMessage(
				FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
				NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
				(LPTSTR) &lpMsgBuf, 0, NULL);
			LogPrint(0, LOG_SYSTEM, 0, "Could not find library function: %s: %s", funcName, lpMsgBuf);
			// Free the buffer.
			LocalFree(lpMsgBuf);
			return NULL;
		}
	#else // WINDOWS
		const char* errmsg;
		dlerror();
		func = (LibraryFunction)dlsym(handle, funcName);
		errmsg = dlerror();
		if (errmsg != NULL) {
			LogPrint(0, LOG_SYSTEM, 0, "Could not find library function: %s: %s", (char*) funcName, (char*) errmsg);
			return NULL;
		}
	#endif // WINDOWS
	return func;
}

Library* OpenLibrary(const char* libName) {
	Library* lib = new Library();
	if (!lib->load(libName)) {
		delete(lib);
		return NULL;
	}
	return lib;
}


//////////////////////////////////////////////////////////////
//                        OS System                         //
//////////////////////////////////////////////////////////////

char OSLocalHostName[1024] = {0};
char OSArchitectureName[1024] = {0};
char OSName[1024] = {0};

const char* GetComputerName() {
	//char name[1024];
	char* name = new char[1024];
	if (strlen(OSLocalHostName) == 0) {
		#ifdef WINDOWS

			DWORD size = 1024;
			#ifdef POCKETPC
				delete [] name;
				return "PocketPC";
			#else
				if (::GetComputerName(name, &size) == 0) {
					delete [] name;
					return "";
				}
				else
					utils::strcpyavail(OSLocalHostName, name, 1024, true);
			#endif

			//if (gethostname(name, 1024) == 0)
			//	strcpy(OSLocalHostName, name);
			//else {
			//	int er = WSAGetLastError();
			//	if (er == WSANOTINITIALISED) {
			//		WSADATA info;
			//		if (WSAStartup(MAKEWORD(1,1), &info) == 0) {
			//			// Now we can retry the socket() function
			//			if (gethostname(hname, 1000) == 0)
			//				strcpy(OSLocalHostName, name);
			//		}
			//	}
			//}
		#else
			std::string outString, errString;
			if (!RunOSCommand("uname -n", NULL, 1000, outString, errString)) {
				//if (!RunOSTextCommand("uname -n", name, size))
				delete [] name;
				return "";
			}
			else
				utils::strcpyavail(OSLocalHostName, outString.c_str(), 1024, true);
		#endif // WINDOWS
	}
	delete [] name;
	return OSLocalHostName;
}

uint64 GetProcessMemoryUsage() {

	#if defined	WINDOWS
		HANDLE hProcess = GetCurrentProcess();
		PROCESS_MEMORY_COUNTERS pmc;
		if (hProcess == NULL)
			return 0;
		if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) != 0)
			return (uint64)pmc.WorkingSetSize;
		else
			return 0;
	#elif defined OSX
		struct task_basic_info_64 info;
		mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
		if (task_info(mach_task_self(), TASK_BASIC_INFO_64, (task_info_t)&info, &count) == KERN_SUCCESS)
			return (uint64)info.resident_size;
		return 0;
	#else
		/* Linux ---------------------------------------------------- */
		long rss = 0L;
		FILE* fp = NULL;
		if ( (fp = fopen( "/proc/self/statm", "r" )) == NULL )
			return (uint64)0L;      /* Can't open? */
		if ( fscanf( fp, "%*s%ld", &rss ) != 1 ) {
			fclose( fp );
			return (uint64)0L;      /* Can't read? */
		}
		fclose( fp );
		return (uint64)rss * (uint64)sysconf( _SC_PAGESIZE);
	#endif
}

uint64 GetPeakProcessMemoryUsage() {

	#if defined	WINDOWS
		HANDLE hProcess = GetCurrentProcess();
		PROCESS_MEMORY_COUNTERS pmc;
		if (hProcess == NULL)
			return 0;
		if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) != 0)
			return (uint64)pmc.PeakWorkingSetSize;
		else
			return 0;
	#elif defined OSX
		// macOS/BSD: ru_maxrss is in bytes (Linux uses KB)
		struct rusage usage;
		getrusage(RUSAGE_SELF, &usage);
		return (uint64)usage.ru_maxrss;
	#else
		// Linux: ru_maxrss is in KB
		struct rusage usage;
		getrusage( RUSAGE_SELF, &usage );
		return ((uint64)usage.ru_maxrss) * 1024L;


		///* AIX and Solaris ------------------------------------------ */
		//struct psinfo psinfo;
		//int fd = -1;
		//if ( (fd = open( "/proc/self/psinfo", O_RDONLY )) == -1 )
		//	return (size_t)0L;      /* Can't open? */
		//if ( read( fd, &psinfo, sizeof(psinfo) ) != sizeof(psinfo) )
		//{
		//	close( fd );
		//	return (size_t)0L;      /* Can't read? */
		//}
		//close( fd );
		//return (size_t)(psinfo.pr_rssize * 1024L);

	#endif
}


uint16 GetCPUCount() {
	#if defined	WINDOWS
		SYSTEM_INFO siSysInfo;
		GetSystemInfo(&siSysInfo); 
		return (uint16) siSysInfo.dwNumberOfProcessors;
	#elif defined OSX
		int ncpu = 0;
		size_t len = sizeof(ncpu);
		if (sysctlbyname("hw.ncpu", &ncpu, &len, NULL, 0) == 0 && ncpu > 0)
			return (uint16)(ncpu > 65535 ? 65535 : ncpu);
		return 0;
	#else
		long n = sysconf(_SC_NPROCESSORS_ONLN);
		if (n > 0)
			return (uint16)(n > 65535 ? 65535 : (uint16)n);
		return 0;
	#endif
}

uint16 GetCPUArchitecture() {
	#if defined	WINDOWS
		SYSTEM_INFO siSysInfo;
		GetSystemInfo(&siSysInfo);
		switch(siSysInfo.wProcessorArchitecture) {
			case PROCESSOR_ARCHITECTURE_AMD64:
				return OSCPU_AMD64;
			case PROCESSOR_ARCHITECTURE_IA64:
				return OSCPU_IA64;
			case PROCESSOR_ARCHITECTURE_INTEL:
				return OSCPU_X86;
			default:
				return OSCPU_UNKNOWN;
		}
	#elif defined OSX
		#if defined(__x86_64__)
			return OSCPU_AMD64;
		#elif defined(__i386__)
			return OSCPU_X86;
		#elif defined(__aarch64__)
			return OSCPU_UNKNOWN;
		#else
			return OSCPU_UNKNOWN;
		#endif
	#else
		#if defined(__x86_64__)
			return OSCPU_AMD64;
		#elif defined(__i386__)
			return OSCPU_X86;
		#elif defined(__aarch64__)
			return OSCPU_UNKNOWN;
		#else
			return OSCPU_UNKNOWN;
		#endif
	#endif
}

uint64 GetCPUSpeed() {
	#if defined	WINDOWS
		//HKEY hKey;
		//LONG lRet;

		// Test for SP6 versus SP6a.
		//JString key = "HARDWARE\\DESCRIPTION\\SYSTEM\\CentralProcessor\\0";
		//lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, &hKey );
		//if( lRet == ERROR_SUCCESS ) {
		//	DWORD len = sizeof(int);
		//	BYTE data[sizeof(int)];
		//	key = "~MHz";
		//	lRet = RegQueryValueEx(hKey, key, NULL, NULL, data, &len);
		//	if (lRet == ERROR_SUCCESS) {
		//		RegCloseKey(hKey);
		//		return (uint64) (data[3] << 24) + (data[2] << 16) + (data[1] << 8) + (data[0] << 0);
		//	}
		//	else {
		//		RegCloseKey(hKey);
		//		return 0;
		//	}
		//}
	#elif defined OSX
		uint64 freq = 0;
		size_t len = sizeof(freq);
		if (sysctlbyname("hw.cpufrequency", &freq, &len, NULL, 0) == 0)
			return freq;
		return 0;
	#else
		// Linux: optional /proc/cpuinfo cpu MHz
		std::ifstream cpuinfo("/proc/cpuinfo");
		std::string line;
		while (cpuinfo && getline(cpuinfo, line)) {
			if (line.compare(0, 7, "cpu MHz") == 0) {
				size_t colon = line.find(':');
				if (colon != std::string::npos) {
					double mhz = 0;
					if (sscanf(line.c_str() + colon + 1, "%lf", &mhz) == 1 && mhz > 0)
						return (uint64)(mhz * 1000000.0);
				}
				break;
			}
		}
	#endif
	return 0;
}

uint64 GetSystemMemorySize() {
	#if defined	WINDOWS
		MEMORYSTATUS memstat;
		GlobalMemoryStatus(&memstat);
		return (uint64)memstat.dwTotalPhys;
	#else
		#ifdef __APPLE__
			uint64 memsize = 0;
			size_t len = sizeof(memsize);
			if (sysctlbyname("hw.memsize", &memsize, &len, NULL, 0) == 0)
				return memsize;
			return 0;
		#else
			struct sysinfo info;
			if (sysinfo(&info) == 0)
				return info.totalram;
			else
				return 0;
		#endif
	#endif
	return 0;
}

bool GetSystemMemoryUsage(uint64 &totalRAM, uint64 &freeRAM) {
	#if defined	WINDOWS
		MEMORYSTATUSEX memstat;
		memset(&memstat, 0, sizeof(MEMORYSTATUSEX));
		memstat.dwLength = sizeof(MEMORYSTATUSEX);
		if (!GlobalMemoryStatusEx(&memstat)) {
			int a = GetLastError();
			return false;
		}
		totalRAM = (uint64)memstat.ullTotalPhys;
		freeRAM = (uint64)memstat.ullAvailPhys;
		return true;
	#else
		#ifdef __APPLE__
			uint64 memsize = 0;
			size_t len = sizeof(memsize);
			if (sysctlbyname("hw.memsize", &memsize, &len, NULL, 0) != 0)
				return false;
			totalRAM = memsize;
			vm_statistics64_data_t vm_stat;
			mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
			if (host_statistics64(mach_host_self(), HOST_VM_INFO64, (host_info64_t)&vm_stat, &count) != KERN_SUCCESS)
				return false;
			vm_size_t page_size;
			if (host_page_size(mach_host_self(), &page_size) != KERN_SUCCESS)
				return false;
			freeRAM = (uint64)vm_stat.free_count * (uint64)page_size;
			return true;
		#else
			struct sysinfo info;
			if (sysinfo(&info) == 0) {
				totalRAM = info.totalram;
				freeRAM = info.freeram;
				return true;
			}
			else
				return false;
		#endif
	#endif
}

const char* GetSystemArchitecture() {
	if (strlen(OSArchitectureName) == 0) {
		#if defined	WINDOWS
			SYSTEM_INFO siSysInfo;
			GetSystemInfo(&siSysInfo); 

			switch(siSysInfo.wProcessorArchitecture) {
				case PROCESSOR_ARCHITECTURE_UNKNOWN:
					utils::strcpyavail(OSArchitectureName, "i386", 1024, true);
				case PROCESSOR_ARCHITECTURE_INTEL:
					switch(siSysInfo.wProcessorLevel) {
						case 3:
							utils::strcpyavail(OSArchitectureName, "i386", 1024, true);
							break;
						case 4:
							utils::strcpyavail(OSArchitectureName, "i486", 1024, true);
							break;
						case 5:
							utils::strcpyavail(OSArchitectureName, "i586", 1024, true);
							break;
						case 6:
							utils::strcpyavail(OSArchitectureName, "i686", 1024, true);
							break;
						case 7:
							utils::strcpyavail(OSArchitectureName, "i786", 1024, true);
							break;
						case 8:
							utils::strcpyavail(OSArchitectureName, "i886", 1024, true);
							break;
						case 9:
							utils::strcpyavail(OSArchitectureName, "i986", 1024, true);
							break;
						default:
							utils::strcpyavail(OSArchitectureName, "i686", 1024, true);
							break;
					}
				case PROCESSOR_ARCHITECTURE_MIPS:
					utils::strcpyavail(OSArchitectureName, "mips", 1024, true);
					break;
				case PROCESSOR_ARCHITECTURE_ALPHA:
					utils::strcpyavail(OSArchitectureName, "alpha", 1024, true);
					break;
				case PROCESSOR_ARCHITECTURE_PPC:
					utils::strcpyavail(OSArchitectureName, "ppc", 1024, true);
					break;
				case PROCESSOR_ARCHITECTURE_IA64:
					utils::strcpyavail(OSArchitectureName, "ia64", 1024, true);
					break;
				case PROCESSOR_ARCHITECTURE_AMD64:
					utils::strcpyavail(OSArchitectureName, "amd64", 1024, true);
					break;
				default:
					break;
			}
		#else
		// ######################################
		#endif
	}
	return OSArchitectureName;
}

const char* GetSystemOSName() {
	if (strlen(OSName) == 0) {
		#if defined	WINDOWS
			#ifdef POCKETPC
				utils::strcpyavail(OSName, "Microsoft PocketPC", 1024, true);
			#else
				switch(GetCPUArchitecture()) {
					case OSCPU_AMD64:
						utils::strcpyavail(OSName, "Microsoft Windows X64", 1024, true);
						break;
					case OSCPU_IA64:
						utils::strcpyavail(OSName, "Microsoft Windows IA64", 1024, true);
						break;
					case OSCPU_X86:
						utils::strcpyavail(OSName, "Microsoft Windows X86", 1024, true);
						break;
					default:
						utils::strcpyavail(OSName, "Microsoft Windows", 1024, true);
						break;
				}
			#endif
		#else
		// ######################################
		#endif
	}
	return OSName;
}

bool GetSystemOSVersion(uint16& major, uint16& minor, uint16& build, char* text, uint16 textSize) {
	major = 0;
	minor = 0;
	build = 0;
	text[0] = 0;
	#if defined	WINDOWS
		#ifdef POCKETPC

			OSVERSIONINFO osvi;
			BOOL bOsVersionInfoEx;

			// Try calling GetVersionEx using the OSVERSIONINFOEX structure.
			// If that fails, try using the OSVERSIONINFO structure.

			ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
			osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

			if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
			{
				osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
				if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) 
					return false;
			}

			major = osvi.dwMajorVersion;
			minor = osvi.dwMinorVersion;
			if (strlen((char*)osvi.szCSDVersion) < textSize)
				utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
		#else

			OSVERSIONINFOEX osvi;
			BOOL bOsVersionInfoEx;

			// Try calling GetVersionEx using the OSVERSIONINFOEX structure.
			// If that fails, try using the OSVERSIONINFO structure.

			ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
			osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);

			if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) )
			{
				osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
				if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) 
					return false;
			}

			switch (osvi.dwPlatformId)
			{
				// Test for the Windows NT product family.
				case VER_PLATFORM_WIN32_NT:

				// Display service pack (if any) and build number.

					if( osvi.dwMajorVersion == 4 && 
						lstrcmpi( osvi.szCSDVersion, "Service Pack 6" ) == 0 )
					{
						HKEY hKey;
						LONG lRet;

						// Test for SP6 versus SP6a.
						lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
							"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009",
							0, KEY_QUERY_VALUE, &hKey );
						if( lRet == ERROR_SUCCESS ) {
							if (textSize > 16)
								utils::strcpyavail(text, "Service Pack 6a", textSize, true);
							build = osvi.dwBuildNumber & 0xFFFF;
						}
						else // Windows NT 4.0 prior to SP6a
						{
							major = (uint16)osvi.dwMajorVersion;
							minor = (uint16)osvi.dwMinorVersion;
							build = (uint16)osvi.dwBuildNumber & 0xFFFF;
							if (strlen((char*)osvi.szCSDVersion) < textSize)
								utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
						}
						RegCloseKey( hKey );
					}
					else // Windows NT 3.51 and earlier or Windows 2000 and later
					{
						major = (uint16)osvi.dwMajorVersion;
						minor = (uint16)osvi.dwMinorVersion;
						build = (uint16)osvi.dwBuildNumber & 0xFFFF;

						if (strlen((char*)osvi.szCSDVersion) < textSize)
							utils::strcpyavail(text, (char*)osvi.szCSDVersion, textSize, true);
					}
					break;
			}

		#endif
	#else
		// ######################################
	#endif
	return true;
}

//bool RunOSTextCommand(const char* cmd, char** result, uint32 size) {
//
//	#ifdef WINDOWS
//		// Use RunOSCommand instead
//		return false;
//	#else
//		fflush(stdin);
//		fflush(stdout);
//		FILE* runfile = popen(cmd, "r");
//		if (runfile == NULL)
//			return false;
//
//		int res;
//		int size = 4096;
//		char buffer[size+1];
//		std::string output;
//		
//		res = fread(buffer, 1, size, runfile);
//		if (res <= 0) {
//			pclose(runfile);
//			return false;
//		}
//		do {
//			buffer[res] = 0;
//			output += buffer;
//		} while (res = fread(buffer, 1, size, runfile));
//
//		pclose(runfile);
//		delete [] buffer;
//		*result = new char[output.length()+1];
//		memcpy(*result, output.c_str(), output.length());
//		result[output.length()] = 0;
//		return true;
//	#endif
//}

//bool GetSystemName(uint32 id, const char* title, char* name, uint32 size) {
//	if ( (name == NULL) || (size < 64) )
//		return false;
//	memset(name, 0, 64);
//	uint32 pos = 0;
//	#ifdef WINDOWS
//		strcpy_s(name, size, "Global\\");
//		strcpy_s(name+(pos = (uint32)strlen(name)), size-pos, title);
//	//	_itoa_s(id, name+(pos = (uint32)strlen(name)), size-pos, 16);
//	#else
//		strncpy(name, "/", size);
//		pos = (uint32)strlen(name);
//		strncpy(name+pos, title, size-pos);
//	#endif
//	Int2Ascii((int32)id, name+(pos = (uint32)strlen(name)), size-pos, 16);
//	return true;
//}

char* Int2Ascii(int64 value, char* result, uint16 size, uint8 base) {
	if (base < 2 || base > 36) {
		result[0] = 0;
		return result;
	}

	char* ptr = result, *ptr1 = result, tmp_char;
	int64 tmp_value;

	do {
		tmp_value = value;
		value /= base;
		*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
	} while ( value );

	// Apply negative sign
	if (tmp_value < 0)
		*ptr++ = '-';
	*ptr-- = '\0';
	while(ptr1 < ptr) {
		tmp_char = *ptr;
		*ptr--= *ptr1;
		*ptr1++ = tmp_char;
	}
	return result;
}

char* Uint2Ascii(uint64 value, char* result, uint16 size, uint8 base) {
	if (base < 2 || base > 36) {
		result[0] = 0;
		return result;
	}

	char* ptr = result, *ptr1 = result, tmp_char;
	int64 tmp_value;

	do {
		tmp_value = value;
		value /= base;
		*ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)];
	} while ( value );

	*ptr-- = '\0';
	while(ptr1 < ptr) {
		tmp_char = *ptr;
		*ptr--= *ptr1;
		*ptr1++ = tmp_char;
	}
	return result;
}

unsigned char* Ascii2UTF16LE(const char* ascii, uint32 len, uint32& size) {
	size = (len * 2) + 4;
	unsigned char* result = new unsigned char[size];
	result[0] = 0xff;
	result[1] = 0xfe;
	uint32 i = 2;
	const char* src = ascii;
	for (uint32 n = 0; n < len; n++) {
		result[i++] = *src;
		result[i++] = 0;
		src++;
	}
	result[i++] = 0;
	result[i++] = 0;
	size = (len * 2) + 2;
	return result;
}


bool GetSocketError(int error, char* errorString, uint16 errorStringMaxSize, bool* isRecoverable) {

	if (errorStringMaxSize < 128)
		return false;

	#ifdef WINDOWS

		if (error == WSANOTINITIALISED) {
			utils::strcpyavail(errorString, "Cannot initialize WinSock!", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAENETDOWN) {
			utils::strcpyavail(errorString, "The network subsystem or the associated service provider has failed", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEAFNOSUPPORT) {
			utils::strcpyavail(errorString, "The specified address family is not supported", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEINPROGRESS) {
			utils::strcpyavail(errorString, "A blocking Windows Sockets 1.1 call is in progress, or the service provider is still processing a callback function", errorStringMaxSize, true);
			*isRecoverable = true;
		}
		else if (error == WSAEMFILE) {
			utils::strcpyavail(errorString, "No more socket descriptors are available", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAENOBUFS) {
			utils::strcpyavail(errorString, "No buffer space is available. The socket cannot be created", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEPROTONOSUPPORT) {
			utils::strcpyavail(errorString, "The specified protocol is not supported", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEPROTOTYPE) {
			utils::strcpyavail(errorString, "The specified protocol is the wrong type for this socket", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAESOCKTNOSUPPORT) {
			utils::strcpyavail(errorString, "The specified socket type is not supported in this address family", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEADDRINUSE) {
			utils::strcpyavail(errorString, "The socket's local address is already in use and the socket was not marked to allow address reuse with SO_REUSEADDR. This error usually occurs during execution of the bind function, but could be delayed until this function if the bind was to a partially wildcard address (involving ADDR_ANY) and if a specific address needs to be committed at the time of this function", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEINVAL) {
			utils::strcpyavail(errorString, "The socket has not been bound with bind", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEISCONN) {
			utils::strcpyavail(errorString, "The socket is already connected", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAENOTSOCK) {
			utils::strcpyavail(errorString, "The descriptor is not a socket", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEOPNOTSUPP) {
			utils::strcpyavail(errorString, "The referenced socket is not of a type that supports the listen operation", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEADDRNOTAVAIL) {
			utils::strcpyavail(errorString, "The specified address is not a valid address for this machine", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEFAULT) {
			utils::strcpyavail(errorString, "The name or namelen parameter is not a valid part of the user address space", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEMFILE) {
			utils::strcpyavail(errorString, "The queue is nonempty upon entry to accept and there are no descriptors available", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEWOULDBLOCK) {
			utils::strcpyavail(errorString, "The socket is marked as nonblocking and no connections are present to be accepted", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAETIMEDOUT) {
			utils::strcpyavail(errorString, "Attempt to connect timed out without establishing a connection", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAENETUNREACH) {
			utils::strcpyavail(errorString, "The network cannot be reached from this host at this time", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEISCONN) {
			utils::strcpyavail(errorString, "The socket is already connected (connection-oriented sockets only)", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAECONNREFUSED) {
			utils::strcpyavail(errorString, "The attempt to connect was forcefully rejected", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEAFNOSUPPORT) {
			utils::strcpyavail(errorString, "Addresses in the specified family cannot be used with this socket", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEADDRNOTAVAIL) {
			utils::strcpyavail(errorString, "The remote address is not a valid address (such as ADDR_ANY)", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAEALREADY) {
			utils::strcpyavail(errorString, "A nonblocking connect call is in progress on the specified socket", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAECONNRESET) {
			utils::strcpyavail(errorString, "Connection was reset", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else if (error == WSAECONNABORTED) {
			utils::strcpyavail(errorString, "Software caused connection abort", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else {
			snprintf(errorString, errorStringMaxSize, "TCP error with no description: %d", error);
			*isRecoverable = false;
		}

		return true;
	#else
		if (error == 100) {
			utils::strcpyavail(errorString, "Cannot initialise socket", errorStringMaxSize, true);
			*isRecoverable = false;
		}
		else {
			utils::strcpyavail(errorString, strerror(error), errorStringMaxSize, true);
			*isRecoverable = false;
		}

		return true;
	#endif
}


int GetLastOSErrorNumber() {
	#ifdef WINDOWS
		int err = WSAGetLastError();
		WSASetLastError(0);
		return err;
	#else
		return errno;
	#endif
}

std::string GetLastOSErrorMessage() {
	#ifdef WINDOWS
		LPVOID lpMsgBuf; //Message Buffer
		DWORD error = GetLastError();
		uint32 len = FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
			NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf, 0, NULL);
		std::string str = (char*)lpMsgBuf;
		LocalFree(lpMsgBuf);
		return str;
	#else
		return utils::StringFormat("Error number: %u", errno);
	#endif
}

bool WaitForSocketWriteability(SOCKET s, int32 timeout) {

	int maxfd = 0;

	// If socket is not valid return true so the next read will report the error
	if (s == INVALID_SOCKET) return true;

	struct timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = 0;

	fd_set wds;
	// create a list of sockets to check for activity
	FD_ZERO(&wds);
	// specify mySocket
	FD_SET(s, &wds);

	#ifdef WINDOWS
	#else
		maxfd = s + 1;
	#endif

	if (timeout > 0) {
		ldiv_t d = ldiv(timeout*1000, 1000000);
		tv.tv_sec = d.quot;
		tv.tv_usec = d.rem;
	}

	// Check for readability
	return( select(maxfd, NULL, &wds, NULL, &tv) > 0);
}

bool WaitForSocketReadability(SOCKET s, int32 timeout) {

	int maxfd = 0;

	// If socket is not valid return true so the next read will report the error
	if (s == INVALID_SOCKET) return true;

	struct timeval tv;
	tv.tv_sec = 0;
	tv.tv_usec = 0;

	fd_set rdds;
	// create a list of sockets to check for activity
	FD_ZERO(&rdds);
	// specify mySocket
	FD_SET(s, &rdds);

	#ifdef WINDOWS
	#else
		maxfd = s + 1;
	#endif

	if (timeout > 0) {
		ldiv_t d = ldiv(timeout*1000, 1000000);
		tv.tv_sec = d.quot;
		tv.tv_usec = d.rem;
	}

	// Check for readability
//	uint64 t2 = GetTimeNow();
	int ret = select(maxfd, &rdds, NULL, NULL, &tv);
	//if (GetTimeAge(t2) > 1000) {
//		LogPrint(0,LOG_NETWORK,0,"************** WaitForSocketReadability(%d) took %s ******************", s, PrintTimeDifString(GetTimeAge(t2)).c_str());
	//}
	return(ret > 0);
}

bool SetSocketNonBlockingMode(SOCKET s) {
	#if defined(WINDOWS)
		unsigned long parm = 1; // 1 = Non-blocking, 0 = Blocking
		ioctlsocket(s, FIONBIO, &parm);
	#else
		long parm = fcntl(s, F_GETFL);
		parm |= O_NONBLOCK;
		fcntl(s, F_SETFL, parm);
	#endif
	return true;
}

bool SetSocketBlockingMode(SOCKET s) {
	#if defined(WINDOWS)
		unsigned long parm = 0; // 1 = Non-blocking, 0 = Blocking
		ioctlsocket(s, FIONBIO, &parm);
	#else
		long parm = fcntl(s, F_GETFL);
		parm &= ~O_NONBLOCK;
		fcntl(s, F_SETFL, parm);
	#endif
	return true;
}

bool LookupIPAddress(const char* name, uint32& address) {
	CHECKNETWORKINIT
	if ( (name == NULL) || (strlen(name) == 0) )
		return false;

	// struct hostent* hent;

	#ifdef WINDOWS
		//hent = gethostbyname(name);
		//if (hent == NULL)
		//	return false;
		//else {
		//	memcpy(&address, hent->h_addr_list[0], sizeof(address));
		//	printf("IP1 of %s = %u.%u.%u.%u    (%u)\n", name, GETIPADDRESSQUAD(address), address);
		//	//return true;
		//}

		//uint32 address2 = 0;
		struct addrinfo hints, *res;
		int error = -1;
		const char *cause = NULL;

		memset(&hints, 0, sizeof(hints));
		//do not set to AI_NUMERICHOST, causes issues with connections...
		//hints.ai_flags = AI_NUMERICHOST; // AI_PASSIVE
		hints.ai_family = AF_INET;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_protocol = IPPROTO_TCP;
		__try {
			error = getaddrinfo(name, NULL, &hints, &res);
		}
		__except (EXCEPTION_CONTINUE_EXECUTION) {
			error = -1;
		}

		if (!error) {
			memcpy(&address, res->ai_addr->sa_data + 2, sizeof(address));
			//printf("IP2 of %s = %u.%u.%u.%u    (%u)\n", name, GETIPADDRESSQUAD(address), address);
			freeaddrinfo(res);
			return true;
		}
		else
			return false;


	#else
		struct addrinfo hints, *res;
		int error;
		const char *cause = NULL;

		memset(&hints, 0, sizeof(hints));
		// hints.ai_flags = AI_NUMERICHOST; // AI_PASSIVE
		hints.ai_family = PF_UNSPEC;
		hints.ai_socktype = SOCK_STREAM;
		error = getaddrinfo(name, NULL, &hints, &res);
		if (!error) {
			memcpy(&address, res->ai_addr->sa_data+2, 4);
			freeaddrinfo(res);
			return true;
		}
		else
			return false;
	#endif // WINDOWS
}

#ifdef WINDOWS
	bool IsNetworkInitialised = false;
	bool CheckNetworkInit() {
		if (IsNetworkInitialised) return true;
		WSADATA info;
		if (WSAStartup(MAKEWORD(1,1), &info) != 0) {
			LogPrint(0, LOG_NETWORK, 0, "Could not start Windows Networking...");
			return false;
		}
		IsNetworkInitialised = true;
		return true;
	}
#endif

bool LookupHostname(uint32 address, char* name, uint32 maxSize) {
	CHECKNETWORKINIT
	struct hostent *pHost;

	unsigned char* addrChars = (unsigned char*)&address;
	//char addrString[20];
	char* addrString = new char[20];
	snprintf(addrString, 20, "%u.%u.%u.%u", addrChars[0], addrChars[1], addrChars[2], addrChars[3]);

	#ifdef WINDOWS
		pHost = gethostbyname(addrString);
		delete [] addrString;
		if (pHost == NULL) {
			return false;
		}
		if (strlen(pHost->h_name) > maxSize-1) {
			return false;
		}
		utils::strcpyavail(name, pHost->h_name, maxSize, true);
		return true;
	#else // WINDOWS

		#ifdef Darwin
			pHost = gethostbyname(addrString);
			delete [] addrString;
			if (pHost == NULL)
				return false;
			else {
				if (strlen(pHost->h_name) > maxSize-1)
					return false;
				utils::strcpyavail(name, pHost->h_name, maxSize, true);
				return true;
			}
		#else // Darwin

			pHost = new struct hostent;
			char bf[2000];
			int er;
			gethostbyname_r(addrString, pHost, bf, 2000, &pHost, &er);
			delete [] addrString;
			if (er != 0) {
				delete pHost;
				return false;
			}
			else {
				if (strlen(pHost->h_name) > maxSize-1)
					return false;
				utils::strcpyavail(name, pHost->h_name, maxSize, true);
				delete pHost;
				return true;
			}

		#endif // Darwin
	#endif // WINDOWS
}

std::string GetLocalHostnameString() {
	char* name = new char[256];
	if (!GetLocalHostname(name, 255)) {
		delete [] name;
		return "";
	}
	
	std::string sname = name;
	delete [] name;
	return sname;
}

bool GetLocalHostname(char* name, uint32 maxSize) {
	CHECKNETWORKINIT

	if (gethostname(name, maxSize) == 0)
		return true;
	else
		return false;

	//JString ipaddress = GetLocalIPAddress();
	//JString hostname = ipaddress;
	//struct hostent* hent;
	//if ( (ipaddress.equals("127.0.0.1")) || (ipaddress.equals("localhost")) ) {
	//	localhostName = "127.0.0.1";
	//	return localhostName;
	//}

	//#ifdef WINDOWS

	//	hent = gethostbyname((char*) ipaddress);
	//	if (hent != NULL)
	//		localhostName = JString(hent->h_name);
	//	else {
	//		int err = WSAGetLastError();
	//		if (DEBUGLEVEL(KITCHENSINK)) {
	//			printf("getLocalHostname(): gethostbyaddr error %d...\n", err);
	//		}
	//		localhostName = ipaddress;
	//		return ipaddress;
	//	}

	//#else // WINDOWS

	//	struct addrinfo hints, *res;
	//	int error;
	//	const char *cause = NULL;

	//	memset(&hints, 0, sizeof(hints));
	//	hints.ai_flags = AI_NUMERICHOST;
	//	hints.ai_family = PF_UNSPEC;
	//	hints.ai_socktype = SOCK_STREAM;
	//	error = getaddrinfo(ipaddress, NULL, &hints, &res);
	//	if (error != 0) {
	//		localhostName = ipaddress;
	//		return ipaddress;
	//	}
	//	else {
	//		if (res->ai_canonname != NULL) {
	//			localhostName = res->ai_canonname;
	//		}
	//		else
	//			localhostName = ipaddress;
	//	}
	//	freeaddrinfo(res);

	//#endif // WINDOWS

	//return localhostName;

}

bool GetLocalMACAddress(uint64& address) {
	address = 0;
	uint32 c;
	NetworkInterfaces* interfaces = GetLocalInterfaces(c);
	for (uint32 i=0; i<c; i++) {
		if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) )
			address = interfaces[i].mac;
	}
	delete [] interfaces;
	return (address != 0);
}

uint64* GetLocalMACAddresses(uint32& count) {
	uint32 c;
	NetworkInterfaces* interfaces = GetLocalInterfaces(c);
	if (!c)
		return NULL;
	uint64* addresses = new uint64[c];
	uint32 p=0;
	for (uint32 i=0; i<c; i++) {
		if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) )
			addresses[p++] = interfaces[i].mac;
	}
	count = p;
	return addresses;
}

bool IsLocalIPAddress(const char* addr) {
	uint32 address;
	if (!LookupIPAddress(addr, address))
		return false;
	return IsLocalIPAddress(address);
}

bool IsLocalIPAddress(uint32& address) {
	CHECKNETWORKINIT
	uint32 count;
	uint32* addresses = GetLocalIPAddresses(count);
	if ((addresses == NULL) || (count == 0)) {
		delete [] addresses;
		return false;
	}

	unsigned int n;
	for (n=0; n<count; n++) {
		if (addresses[n] == address) {
			delete [] addresses;
			return true;
		}
	}
	delete [] addresses;
	return false;
}

bool GetLocalIPAddress(uint32& address) {
	CHECKNETWORKINIT
	uint32 count;
	uint32* addresses = GetLocalIPAddresses(count);
	if ((addresses == NULL) || (count == 0)) {
		delete [] addresses;
		return false;
	}
	// First find first non-localhost address
	unsigned int n;
	bool localhostPresent = false;
	for (n=0; n<count; n++) {
		if (addresses[n] != LOCALHOSTIP) {
			if (addresses[n] != 0) {
				address = addresses[n];
				delete [] addresses;
				return true;
			}
		}
		else
			localhostPresent = true;
	}
	delete [] addresses;
	// OK, then accept localhost, if present
	if (localhostPresent) {
		address = LOCALHOSTIP;
		return true;
	}
	else
		return false;
}

uint32* GetLocalIPAddresses(uint32& count) {
	CHECKNETWORKINIT

	uint32 maxSize = 128;
	uint32* addresses = new uint32[maxSize];

	addresses[0] = LOCALHOSTIP;
	count = 1;

	char* szHostName = new char[255];

	bool canAskHost = true;
	if( gethostname(szHostName, 255) != 0 ) {
		#ifdef WINDOWS
			int err = GetLastOSErrorNumber();
			if (err == WSANOTINITIALISED) {
				WSADATA info;
				if (WSAStartup(MAKEWORD(1,1), &info) != 0) {
					delete [] szHostName;
					return addresses;
				}
				else {
					if( gethostname(szHostName, 255) != 0 )
						canAskHost = false;
				}
			}
			else
				canAskHost = false;
		#else // WINDOWS
			canAskHost = false;
		#endif // WINDOWS
	}

	struct hostent * pHost;
	uint32 addr, j, i;
	bool exists;

	if (canAskHost) {
		// Get host addresses

		#ifdef WINDOWS
			pHost = gethostbyname(szHostName);
		#else // WINDOWS

			#ifdef Darwin
				pHost = gethostbyname(szHostName);
				//if (pHost == NULL)
				//	return addresses;
			#else // Darwin

				pHost = new struct hostent;
				char* bf = new char[2000];
				int er;
				int res = gethostbyname_r(szHostName, pHost, bf, 2000, &pHost, &er);
				// LogPrint(0, LOG_NETWORK, 0, "gethostbyname_r '%s' [%d] [%d]...\n", szHostName, res, er);
				if ((er != 0) && (res == 0)) {
					delete pHost;
					pHost = NULL;
					// return addresses;
				}
				delete [] bf;
			#endif // Darwin
		#endif // WINDOWS

		for( i = 0; pHost!= NULL && pHost->h_addr_list[i]!= NULL; i++ )
		{
			exists = false;
			memcpy(&addr, ((unsigned char*)pHost->h_addr_list[i]), sizeof(uint32));
			if ( (addr != 0) && (addr != LOCALHOSTIP) ) {
				for (j=0; j<count; j++) {
					if (addresses[j] == addr) {
						exists = true;
						break;
					}
				}
				if (!exists)
					addresses[count++] = addr;
			}
		}

		#ifdef WINDOWS
		#else // WINDOWS
			#ifdef Darwin
				endhostent();
			#else
				delete pHost;
			#endif // Darwin
		#endif // WINDOWS
	}

	uint32 c;
	NetworkInterfaces* interfaces = GetLocalInterfaces(c);
	for (i=0; i<c; i++) {
		exists = false;
		if ( (interfaces[i].address != 0) && (interfaces[i].address != LOCALHOSTIP) ) {
			for (j=0; j<count; j++) {
				if (addresses[j] == interfaces[i].address) {
					exists = true;
					break;
				}
			}
			if (!exists)
				addresses[count++] = interfaces[i].address;
		}
	}
	delete [] interfaces;
	delete [] szHostName;
	return addresses;
}

NetworkInterfaces* GetLocalInterfaces(uint32& count) {
	CHECKNETWORKINIT

	uint32 maxSize = 128;
	uint32 addr;
	NetworkInterfaces* interfaces = new NetworkInterfaces[maxSize];
	count = 0;

	#ifdef WINDOWS

		DWORD dwSize = 0;
		DWORD dwRetVal = 0;

		unsigned int i = 0;

		// Set the flags to pass to GetAdaptersAddresses
		ULONG flags = GAA_FLAG_INCLUDE_PREFIX;

		// default to unspecified address family (both)
		ULONG family = AF_INET;

		LPVOID lpMsgBuf = NULL;

		PIP_ADAPTER_ADDRESSES pAddresses = NULL;
		ULONG outBufLen = 0;
		ULONG Iterations = 0;

		PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
		PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;
		PIP_ADAPTER_ANYCAST_ADDRESS pAnycast = NULL;
		PIP_ADAPTER_MULTICAST_ADDRESS pMulticast = NULL;
		IP_ADAPTER_DNS_SERVER_ADDRESS *pDnServer = NULL;
		IP_ADAPTER_PREFIX *pPrefix = NULL;

		// Allocate a 15 KB buffer to start with.
		outBufLen = 15000;

		do {

			pAddresses = (IP_ADAPTER_ADDRESSES *) MALLOC(outBufLen);
			if (pAddresses == NULL)
				return interfaces;

			dwRetVal =
				GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);

			if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
				FREE(pAddresses);
				pAddresses = NULL;
			}
			else
				break;

			Iterations++;

		} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < 10));


		if (dwRetVal == NO_ERROR) {
			// If successful, output some information from the data we received
			pCurrAddresses = pAddresses;
			while (pCurrAddresses) {
				if ( (pCurrAddresses->OperStatus == IfOperStatusUp) && (pCurrAddresses->IfIndex != 0)) {

					// First check the IP address
					pUnicast = pCurrAddresses->FirstUnicastAddress;
					if (pUnicast != NULL) {
						for (i = 0; pUnicast != NULL; i++) {
							addr = *(uint32*)(((unsigned char*) ((SOCKADDR*)(pUnicast->Address.lpSockaddr))->sa_data) + 2);
							if ( (addr != 0) && (addr != LOCALHOSTIP) ) {
								interfaces[count].address = addr;
								interfaces[count].mac = 0;
								if (pCurrAddresses->PhysicalAddressLength == 6)
									memcpy(&(interfaces[count].mac), &(pCurrAddresses->PhysicalAddress), 6);
								WideCharToMultiByte( CP_ACP, 0, pCurrAddresses->Description, -1, interfaces[count].name, MAXKEYNAMELEN, NULL, NULL );
								WideCharToMultiByte( CP_ACP, 0, pCurrAddresses->FriendlyName, -1, interfaces[count].friendlyName, MAXKEYNAMELEN, NULL, NULL );
								count++;
							}
							pUnicast = pUnicast->Next;
						}
					}
				}

				pCurrAddresses = pCurrAddresses->Next;
			}
		}
		if (pAddresses)
			FREE(pAddresses);

		return interfaces;

	#else
		int sock = 0;
		struct ifreq ifreq;
		struct sockaddr_in *saptr = NULL;
		struct if_nameindex *iflist = NULL, *listsave = NULL;

		//need a socket for ioctl()
		if( (sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
			return interfaces;
		}

		//returns pointer to dynamically allocated list of structs
		iflist = listsave = if_nameindex();

		if (iflist == NULL) {
			close(sock);
			return interfaces;
		}

		//walk thru the array returned and query for each
		//interface's address
		//for(iflist; iflist->if_index != 0; iflist++) {
		for(; iflist->if_index != 0; iflist++) {
			//copy in the interface name to look up address of
			strncpy(ifreq.ifr_name, iflist->if_name, IF_NAMESIZE);
			//get the address for this interface
			if(ioctl(sock, SIOCGIFADDR, &ifreq) != 0) {
				// ignore;
				continue;
			}
			//print out the address
			saptr = (struct sockaddr_in *)&ifreq.ifr_addr;
			//unsigned char* ip = (unsigned char*)&(saptr->sin_addr.s_addr);
			//printf("**** %u.%u.%u.%u ***\n", ip[0], ip[1], ip[2], ip[3]);
			memcpy(&(interfaces[count].address), &(saptr->sin_addr.s_addr), sizeof(uint32));
			utils::strcpyavail(interfaces[count].name, ifreq.ifr_name, MAXKEYNAMELEN, true);
			#ifdef __APPLE__
			interfaces[count].mac = 0;
			#else
			// get the MAC address (Linux only)
			interfaces[count].mac = 0;
			ioctl(sock, SIOCGIFHWADDR, &ifreq);
			memcpy(&(interfaces[count].mac), &(ifreq.ifr_hwaddr.sa_data), 6);
			#endif
			count++;
		}
		//free the dynamic memory kernel allocated for us
		if_freenameindex(listsave);
		close(sock); 

	#endif

	return interfaces;
}

bool GetNextAvailableLocalPort(uint16 lastPort, uint16 &nextPort) {
	CHECKNETWORKINIT

	SOCKET socket;
	struct	sockaddr_in	addr;
	addr.sin_family= AF_INET;
	addr.sin_addr.s_addr=INADDR_ANY;
	bool result = false;

	nextPort = lastPort + 1;
	while (nextPort < lastPort + 1000) {
		if((socket=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET)
			return false;
  
		#ifdef WINDOWS
			// Set the exclusive address option, preventing other software binding to
			// non-INADDR_ANY (i.e. interface addresses such as localhost directly)
			int one = 1;
			setsockopt(socket, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &one, sizeof(one));
		#else
			/*
				This socket option tells the kernel that even if this port is busy (in
				the TIME_WAIT state), go ahead and reuse it anyway.  If it is busy,
				but with another state, you will still get an address already in use
				error.  It is useful if your server has been shut down, and then
				restarted right away while sockets are still active on its port.  You
				should be aware that if any unexpected data comes in, it may confuse
				your server, but while this is possible, it is not likely.
			*/
			int one = 1;
			setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&one,sizeof(one));
		#endif

		addr.sin_port=htons(nextPort);

		result = (bind(socket,(SOCKADDR*)&addr,sizeof(struct sockaddr_in))!=SOCKET_ERROR);
		shutdown(socket, SD_BOTH);
		closesocket(socket);

		if (result)
			return true;

		nextPort++;
	}
	return false;

}

bool UtilsTest() {
	printf("Testing Utils...\n\n");

	char hostname[1024];
	if (!GetLocalHostname(hostname, 1023)) {
		printf("Error GetLocalHostname\n");
		return false;
	}
	printf("Local Hostname: '%s'\n\n", hostname);

	uint32 addr;
	unsigned char* addrChars = (unsigned char*) &addr;
	if (!GetLocalIPAddress(addr)) {
		printf("Error GetLocalIPAddress\n");
		return false;
	}
	printf("Local IP Address: %u.%u.%u.%u\n\n", addrChars[0], addrChars[1], addrChars[2], addrChars[3]);

	uint32 count;
	uint32* addresses = GetLocalIPAddresses(count);
	if (addresses == NULL) {
		printf("Error GetLocalIPAddresses\n");
		return false;
	}
	uint32 n;
	for (n=0; n<count; n++) {
		addrChars = (unsigned char*) &addresses[n];
		printf("Local IP Address[%u]: %u.%u.%u.%u (%u,%u)\n\n", n, addrChars[0], addrChars[1], addrChars[2], addrChars[3], addresses[n], LOCALHOSTIP);
	}
	delete [] addresses;

	NetworkInterfaces* interfaces = GetLocalInterfaces(count);
	for (n=0; n<count; n++) {
		addrChars = (unsigned char*) &(interfaces[n]);
		printf("Local Interface[%u] %s: %u.%u.%u.%u (%llX)\n\n", n, interfaces[n].name,
			addrChars[0], addrChars[1], addrChars[2], addrChars[3], interfaces[n].mac);
	}
	delete [] interfaces;

	return true;
}

void PrintBinary(void* p, uint32 size, bool asInt, const char* title) {
	if (title != NULL)
		printf("--- %s %u ---\n", title, size);
	unsigned char c;
	for (uint32 n=0; n<size; n++) {
		c = *(((unsigned char*)p)+n);
		if (asInt)
			printf("[%u] ", (unsigned int)c);
		else
			printf("[%c] ", c);
		if ( (n > 0) && ((n+1)%10 == 0) )
			printf("\n");
	}
	printf("\n");
}

// const char* stristr(const char *str, const char *substr, uint32 len) {
// 	if (!str || !substr) return NULL;
// 	const char *a, *b;
// 	uint32 n=0, m, sslen = (uint32)strlen(substr);
// 	for(;*str;*str++,n++) {
// 		a = str;
// 		b = substr;
// 		m = n+sslen;
// 		while((*a++ | 32) == (*b++ | 32)) {
// 			if(!*b)
// 				return str;
// 			if (len && (++m > len))
// 				return NULL;
// 		}
// 	}
// 	return NULL;
// }


const char* stristr(const char *str, const char *substr, uint32 len) {
	if (!str || !substr) return NULL;
	uint32 sslen = (uint32)strlen(substr);
	if (sslen == 0) return (const char*)str;
	const char *a, *b;
	uint32 n=0, m;
	for (; *str; str++, n++) {
		a = str;
		b = substr;
		m = n+sslen;
		/* Case-insensitive compare via |32 (ASCII A–Z only). */
		while ((*a++ | 32) == (*b++ | 32)) {
			if (!*b)
				return str;
			if (len && (++m > len))
				return NULL;
		}
	}
	return NULL;
}

uint32 strcpyavail(char* dst, const char* src, uint32 maxlen, bool copyAvailable) {
	if ( !dst || ! src || !maxlen )
		return 0;
	uint32 len = (uint32)strlen(src);
	if (len > maxlen-1) {
		if (!copyAvailable)
			return 0;
		else {
			memcpy(dst, src, maxlen-1);
			dst[maxlen] = 0;
			return maxlen-1;
		}
	}
	else
		memcpy(dst, src, len+1);
	return len;
}

bool GetNextLineEnd(const char *str, uint32 size, uint32& len, uint32& crSize) {
	if (!str || !size)
		return false;

	for (len = 0; len < size; len++) {
		// Checking for CR and CRLF
		if (str[len] == 13) {
			crSize = ( (len < size-1) && (str[len+1] == 10 ) ) ? 2 : 1;
			return true;
		}
		// Checking for LF only without CR
		if (str[len] == 10) {
			crSize = 1;
			return true;
		}
	}
	return false;
}

std::string TextCapitalise(const char* text) {
	if (!text)
		return "";
	std::string str = text;
	if (!str.length())
		return "";
	std::string::iterator i = str.begin(), e = str.end();
	*i = ::toupper(*i);
	i++;
	if (i != e)
		std::transform(i, e, i, ::tolower);
	return str;
}

std::string TextUppercase(const char* text) {
	std::string str = text;
	std::transform(str.begin(), str.end(), str.begin(), ::toupper);
	return str;
}

std::string TextLowercase(const char* text) {
	std::string str = text;
	std::transform(str.begin(), str.end(), str.begin(), ::tolower);
	return str;
}

std::string TextIndent(const char* text, const char* indent) {
	std::vector<std::string> lines = utils::TextListSplitLines(text, true, false);
	std::vector<std::string>::iterator i = lines.begin(), e = lines.end();
	std::string output;
	while (i != e) {
		output += utils::StringFormat("%s%s\n", indent, (*i).c_str());
		i++;
	}
	return output;
}

std::string TextUnindent(const char* text) {
	uint32 len = (uint32)strlen(text);
	if (!len) return "";
	if (text[0] > 32)
		return text;

	// Find initial spacing char sequence
	uint32 s = 0, p = 0;
	// Find initial line endings
	while ( ((text[s] == '\n') || (text[s] == '\r')) && (s < len))
		s++;
	p = s;
	while ((text[p] <= 32) && (p < len))
		p++;
	std::string indentString = std::string(text+s, p-s);
	std::string str = text;
	StringSingleReplace(str, indentString, "", false);
	return str;
}


std::string TextTrimQuotes(const char* text) {
	if (text) {
		uint32 begin = 0, end = (uint32)strlen(text);
		if (begin == end)
			return "";
		while ((begin < end) && (text[begin] <= 32) || (text[begin] == '\"') || (text[begin] == '\''))
			begin++;
		if (begin == end)
			return "";
		while ((end > begin) && (text[end-1] <= 32) || (text[end-1] == '\"') || (text[end-1] == '\''))
			end--;
		return std::string(text+begin, end-begin);
	}
	else
		return "";
}


std::string TextTrim(const char* text) {
	if (text) {
		uint32 begin = 0, end = (uint32)strlen(text);
		if (begin == end)
			return "";
		while ((text[begin] <= 32) && (begin < end))
			begin++;
		while ((end > begin) && (text[end-1] <= 32))
			end--;
		return std::string(text+begin, end-begin);
	}
	else
		return "";
}

std::multimap<std::string, std::string> TextMultiMapSplit(const char* text, const char* outersplit, const char* innersplit) {
	std::multimap<std::string, std::string> result;
	typedef std::pair <std::string, std::string> Map_String_Pair;

	if (!text || !outersplit || !innersplit)
		return result;

	const char* pstr = text, *lstr = text;
	const char* src = text;

	while (pstr = strstr(src, outersplit)) {
		if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
			// result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
			result.insert(Map_String_Pair(TextTrim(std::string(src, lstr-src).c_str()), TextTrim(std::string(lstr+1, pstr-lstr-1).c_str())));
		}
		src = pstr + 1;
	}
	pstr = src + strlen(src);
	if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
		// result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
		result.insert(Map_String_Pair(TextTrim(std::string(src, lstr-src).c_str()), TextTrim(std::string(lstr+1, pstr-lstr-1).c_str())));
	}

	return result;
}


std::map<std::string, std::string> TextMapSplit(const char* text, const char* outersplit, const char* innersplit) {
	std::map<std::string, std::string> result;
	typedef std::pair <std::string, std::string> Map_String_Pair;

	if (!text || !outersplit || !innersplit)
		return result;

	const char* pstr = text, *lstr = text;
	const char* src = text;

	while (pstr = strstr(src, outersplit)) {
		if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
			// result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
			result.insert(Map_String_Pair(TextTrim(std::string(src, lstr - src).c_str()), TextTrim(std::string(lstr + 1, pstr - lstr - 1).c_str())));
		}
		src = pstr + 1;
	}
	pstr = src + strlen(src);
	if ((lstr = strstr(src, innersplit)) && (lstr < pstr)) {
		// result[TextTrim(std::string(src, lstr-src).c_str())] = TextTrim(std::string(lstr+1, pstr-lstr-1).c_str());
		result.insert(Map_String_Pair(TextTrim(std::string(src, lstr - src).c_str()), TextTrim(std::string(lstr + 1, pstr - lstr - 1).c_str())));
	}

	return result;
}

char** SplitCommandline(const char* cmdline, int& argc) {
	std::vector<std::string> args = TextCommandlineSplit(cmdline);
	uint32 size = (uint32)args.size();
	if (!size)
		return NULL;

	char** argv = new char*[size+1];
	uint32 c = 0;
	std::vector<std::string>::iterator i = args.begin(), e = args.end();
	while (i != e) {
		argv[c] = new char[(*i).length()+1];
		strcpy(argv[c], (*i).c_str());
		c++;
		i++;
	}
	argv[c] = NULL;
	argc = (int)c;
	return argv;
}

wchar_t** SplitCommandlineW(const char* cmdline, int& argc) {
	std::vector<std::string> args = TextCommandlineSplit(cmdline);
	uint32 size = (uint32)args.size();
	if (!size)
		return NULL;

	wchar_t** argv = new wchar_t*[size+1];
	uint32 c = 0;
	std::vector<std::string>::iterator i = args.begin(), e = args.end();
	while (i != e) {
		argv[c] = new wchar_t[(*i).length()+1];
		mbstowcs(argv[c], (*i).c_str(), (*i).length() + 1);
		c++;
		i++;
	}
	argv[c] = NULL;
	argc = (int)c;
	return argv;
}

bool DeleteCommandline(char** argv, int argc) {
	for (uint32 n=0; n<(uint32)argc; n++)
		delete [] argv[n];
	delete [] argv;
	return true;
}

std::vector<std::string> TextCommandlineSplit(const char* cmdline) {

	bool insideQuotes = false;
	bool lastWasWhiteSpace = false;
	std::vector<std::string> vect;
	std::string arg, val, str;
	unsigned char ch;

	uint32 len = (uint32)strlen(cmdline);

	bool keepQuotes = false;

	for (uint32 n=0; n<len; n++) {
		ch = cmdline[n];
		if (ch <= 32) {
			if (!insideQuotes) {
				if (!lastWasWhiteSpace) {
					// An arg is ended, parse it...
					vect.push_back(str);
					str = "";
				}
				else {
					// Ignore this whitespace then...
				}
				lastWasWhiteSpace = true;
			}
			else {
				str += ch;
			}
		}
		else if (ch == '"') {
			insideQuotes = !insideQuotes;
			if (keepQuotes)
				str += ch;
			lastWasWhiteSpace = false;
		}
		else {
			str += ch;
			lastWasWhiteSpace = false;
		}
	}

	if (str.length() > 0) {
		vect.push_back(str);
		str = "";
	}

	return vect;
}

std::vector<std::string> TextListSplitLines(const char* text, bool keepEmpty, bool autoTrim) {
	if (!text || !strlen(text))
		return std::vector<std::string>();

	if (strstr(text, "\r\n"))
		return TextListSplit(text, "\r\n", keepEmpty, autoTrim);
	else if (strstr(text, "\n\r"))
		return TextListSplit(text, "\n\r", keepEmpty, autoTrim);
	else if (strstr(text, "\n"))
		return TextListSplit(text, "\n", keepEmpty, autoTrim);
	else if (strstr(text, "\r"))
		return TextListSplit(text, "\n", keepEmpty, autoTrim);

	std::vector<std::string> result;
	result.push_back(text);
	return result;
}

std::vector<std::string> TextListBreakLines(const char* text, uint32 maxLineLength) {
	if (!text || !strlen(text))
		return std::vector<std::string>();

	std::string str;
	std::vector<std::string> result;
	uint32 s = 0, n = 0, len = (uint32)strlen(text);
	int p = -1;
	while (n < len) {
		if (text[n] == 13) {
			if (n == s)
				result.push_back("");
			else {
				str = std::string(text + s, n - s);
				result.push_back(TextTrim(str.c_str()));
			}
			if ((n < len - 1) && (text[n + 1] == 10))
				s = n + 2;
			else
				s = n + 1;
			p = -1;
		}
		else if (text[n] == 10) {
			if (n == s)
				result.push_back("");
			else {
				str = std::string(text + s, n - s);
				result.push_back(TextTrim(str.c_str()));
			}
			if ((n < len - 1) && (text[n + 1] == 13))
				s = n + 2;
			else
				s = n + 1;
			p = -1;
		}
		else if ((n-s < maxLineLength) && (text[n] == 32)) {
			p = n - s;
		}
		else if (n - s >= maxLineLength) {
			if (p == 0) {
				result.push_back("");
				s += p + 1;
				p = -1;
				n = s - 1;
			}
			else if (p > 0) {
				str = std::string(text + s, p);
				result.push_back(TextTrim(str.c_str()));
				s += p + 1;
				p = -1;
				n = s - 1;
			}
			else {
				str = std::string(text + s, maxLineLength);
				result.push_back(TextTrim(str.c_str()));
				s += maxLineLength;
				p = -1;
				n = s;
			}
		}
		n++;
	}
	if (s < n) {
		str = std::string(text + s, n - s);
		result.push_back(TextTrim(str.c_str()));
	}

	return result;
}

std::string TextListBreakLines(const char* text, uint32 maxLineLength, const char* breakstr) {
	std::vector<std::string> lines = TextListBreakLines(text, maxLineLength);
	if (!breakstr)
		return TextJoin(lines, "\n");
	else
		return TextJoin(lines, breakstr);
}

std::string TextJoin(std::vector<std::string> &list, const char* split, uint32 start, uint32 count) {
	std::ostringstream o;
	std::vector<std::string>::iterator i = list.begin(), e = list.end();
	uint32 l = 0, c = 0;
	while (i != e) {
		if (!count || ((l >= start) && (c < count))) {
			if (c++) o << split;
			o << *i;
		}
		l++;
		i++;
	}
	return o.str();
}

std::string TextJoin(std::list<std::string> &list, const char* split, uint32 start, uint32 count) {
	std::ostringstream o;
	std::list<std::string>::iterator i = list.begin(), e = list.end();
	uint32 l = 0, c = 0;
	while (i != e) {
		if (!count || ((l >= start) && (c < count))) {
			if (c++) o << split;
			o << *i;
		}
		l++;
		i++;
	}
	return o.str();
}

std::string TextJoin(std::map<std::string, std::string> &map, const char* innersplit, const char* outersplit, bool keyquotes, bool valquotes) {
	std::ostringstream o;
	std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
	uint32 c = 0;
	while (i != e) {
		if (c++) o << outersplit;
		if (keyquotes && valquotes)
			o << "\"" << i->first << "\"" << innersplit << "\"" << i->second << "\"";
		else if (valquotes)
			o << i->first << innersplit << "\"" << i->second << "\"";
		else
			o << i->first << innersplit << i->second;
		i++;
	}
	return o.str();
}

std::string TextJoinJSON(std::map<std::string, std::string> &map) {
	std::ostringstream o;
	std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
	uint32 c = 0;
	o << "{ ";
	while (i != e) {
		if (c++)
			o << ", \"" << utils::EncodeJSON(i->first) << "\": \"" << utils::EncodeJSON(i->second) << "\"";
		else
			o << " \"" << utils::EncodeJSON(i->first) << "\": \"" << utils::EncodeJSON(i->second) << "\"";
		i++;
	}
	o << " }";
	return o.str();
}

std::string TextJoinXML(std::map<std::string, std::string> &map, const char* innernodeName, const char* outernodeName) {
	if (!innernodeName || !strlen(innernodeName))
		return "";
	std::ostringstream o;
	std::map<std::string, std::string>::iterator i = map.begin(), e = map.end();
	if (outernodeName && strlen(outernodeName))
		o << "<" << outernodeName << ">\n";
	while (i != e) {
		o << "<" << innernodeName << " " << html::EncodeHTML(i->first) << "=\"" << html::EncodeHTML(i->second) << "\" />\n";
		i++;
	}
	if (outernodeName && strlen(outernodeName))
		o << "</" << outernodeName << ">\n";
	return o.str();
}



std::vector<std::string> TextListSplit(const char* text, const char* split, bool keepEmpty, bool autoTrim) {
	std::vector<std::string> result;
	if (!text || !split)
		return result;

	uint32 splitLen = (uint32)strlen(split);
	const char* pstr = text;
	const char* src = text;

	while (pstr = strstr(src, split)) {
		if (pstr - src || keepEmpty) {
			if (autoTrim)
				result.push_back(TextTrim(std::string(src, pstr - src).c_str()));
			else
				result.push_back(std::string(src, pstr - src).c_str());
		}
		src = pstr + splitLen;
	}
	pstr = src + strlen(src);
	if (pstr - src) {
		if (autoTrim)
			result.push_back(TextTrim(std::string(src, pstr - src).c_str()));
		else
			result.push_back(std::string(src, pstr - src).c_str());
	}
	return result;
}

std::string TextVectorConcat(std::vector<std::string> vect, const char* sep, bool allowEmpty) {
	std::string result;
	std::vector<std::string>::iterator i, e;
	for (i=vect.begin(), e=vect.end(); i!=e; i++) {
		if (allowEmpty || (*i).size()) {
			if (result.size())
				result += sep;
			result += *i;
		}
	}
	return result;
}

char* StringFormat(uint32& size, const char *format, ...) {
	if (strlen(format) == 0) {
		size = 0;
		return 0;
	}

	va_list args;
	va_start(args, format);
	char* res = StringFormatVA(size, format, args);
	va_end(args);
	return res;
}


char* StringFormatVA(uint32& size, const char *format, va_list orig_args) {
	char* str = NULL;

	#ifdef WINDOWS
		int len = _vscprintf(format, orig_args) + 10;
		str = (char*)malloc(len+1);
		#ifdef CYGWIN
			len = vsnprintf(str, len, format, orig_args);
		#else
			len = _vsnprintf_s(str, len, len+1, format, orig_args);
		#endif

		if (len)
			size = (uint32)strlen(str);
		else {
			size = 0;
			free((char*)str);
			str = NULL;
		}
	#else // WINDOWS
		int len = vasprintf(&str, format, orig_args);
		if(len >= 0)
			size = strlen(str);
		else
			size = 0;
	#endif //HAVE_VASPRINTF

	return str;
}

bool StringFormatInto(char* dst, uint32 maxsize, const char *format, ...) {
	if (!dst || !maxsize)
		return false;
	if (strlen(format) == 0) {
		dst[0] = 0;
		return true;
	}

	uint32 len = 0;
	va_list args;
	va_start(args, format);
	char* str = StringFormatVA(len, format, args);
	va_end(args);
	if (str && len) {
		if (len > maxsize) {
			free(str);
			return false;
		}
		else {
			utils::strcpyavail(dst, str, maxsize, true);
			free(str);
			return true;
		}
	}
	else {
		free(str);
		return false;
	}
}

std::string StringFormat(const char *format, ...) {
	if (strlen(format) == 0)
		return "";

	std::string res;
	uint32 len = 0;
	va_list args;
	va_start(args, format);
	char* str = StringFormatVA(len, format, args);
	va_end(args);
	if (str && len)
		res = str;
	else
		res = "";
	free(str);
	return res;
}

uint32 StringSingleReplace(std::string& text, std::string key, std::string value, bool onlyFirst) {
	if (!text.size() || !key.size())
		return 0;

	uint32 c = 0;
	size_t p = 0;
	uint32 keylen = (uint32)key.size();

	while ( (p = text.find(key, p)) != std::string::npos) {
		text.replace(p, keylen, value);
		p++;
		c++;
		if (onlyFirst)
			break;
	}
	return c;
}

uint32 StringMultiReplace(std::string& text, std::map<std::string, std::string>& map, bool onlyFirst) {
	if (!text.size() || !map.size())
		return 0;

	uint32 c = 0;

	std::map<std::string, std::string>::iterator it, itEnd;
	for (it = map.begin(), itEnd = map.end(); it != itEnd; ++it)
		c += StringSingleReplace(text, it->first, it->second, onlyFirst);
	
	return c;
}


// Example use
	//uint32 len;
	//char* file = utils::ReadAFile("test.html", len);
	//std::string sfile = file;

	//std::list<std::map<std::string, std::string> > list;

	//char tmp[64];
	//std::map<std::string, std::string> map;
	//for (uint32 n=0; n<10; n++) {
	//	map["%1%"] = utils::Int2Ascii(n, tmp, 64, 10);
	//	map["%2%"] = "Hello2";
	//	map["%3%"] = "Hello3";
	//	map["%4%"] = "Hello4";
	//	map["%5%"] = "Hello5";
	//	list.push_back(map);
	//}

	//utils::ScriptReplace(sfile, "test", list);

uint32 StringScriptReplace(std::string& text, std::string name, std::list<std::map<std::string, std::string> >& list) {
	if (!text.size() || !name.size())
		return 0;

	uint32 c = 0;

	char* start = new char[name.size() + 50];
	snprintf(start, name.size()+50, "<!-- start:%s -->", name.c_str());
	char* end = new char[name.size() + 50];
	snprintf(end, name.size()+50, "<!-- end:%s -->", name.c_str());

	// Find the section boundaries
	size_t a1 = text.find(start);
	if (a1 == std::string::npos) {
		delete [] start;
		delete [] end;
		return 0;
	}
	size_t b1 = text.find(end);
	if (b1 == std::string::npos) {
		delete [] start;
		delete [] end;
		return 0;
	}

	uint32 a2 = (uint32)(a1 + strlen(start));
	uint32 b2 = (uint32)(b1 + strlen(end));

	// Copy it
	std::string section = text.substr(a2, b1-a2);
	std::string tmp;
	std::string result;

	std::list<std::map<std::string, std::string> >::iterator it, itEnd;
	for (it = list.begin(), itEnd = list.end(); it != itEnd; ++it) {
		tmp = section;
		c += StringMultiReplace(tmp, *it, false);
		result += tmp;
	}
	
	text.replace(a1, b2-a1, result);
	delete [] start;
	delete [] end;
	return c;
}



unsigned char Hex2Char(const char* str) {
	unsigned char c = (unsigned char) strtol(str, NULL, 16);
	if (c == 0)
		return ' ';
	else
		return c;
}

unsigned char Dec2Char(const char* str) {
	unsigned char c = (unsigned char) strtol(str, NULL, 10);
	if (c == 0)
		return ' ';
	else
		return c;
}

int8 CompareFloats(float64 a, float64 b) {
	float64 diff = a - b;
	if ((diff < std::numeric_limits<float64>::epsilon()) && (-diff < std::numeric_limits<float64>::epsilon()))
		return 0;
	else if (diff > 0)
		return 1;
	else
		return -1;
}


#define poly 0xEDB88320
/* Some compilers need
   #define poly 0xEDB88320uL
 */

/* On entry, addr=>start of data
			 num = length of data
			 crc = incoming CRC     */
int CRC32(const char *addr, uint32 length, int32 crc) {
	uint32 i;
	for (; length>0; length--) {			/* Step through bytes in memory */
		crc = crc ^ *addr++;				/* Fetch byte from memory, XOR into CRC */
		for (i=0; i<8; i++) {				/* Prepare to rotate 8 bits */
			if (crc & 1)					/* b0 is set... */
				crc = (crc >> 1) ^ poly;	/* rotate and XOR with ZIP polynomic */
			else							/* b0 is clear... */
				crc >>= 1;					/* just rotate */
											/* Some compilers need:
											crc &= 0xFFFFFFFF;
											*/
		}									/* Loop for 8 bits */
	}										/* Loop until num=0 */
	return(crc);							/* Return updated CRC */
}


std::string ReadAFileString(std::string filename) {
	uint32 length;
	char* data = ReadAFile(filename.c_str(), length, false);
	if (!data || !length)
		return "";
	std::string dataString = data;
	delete [] data;
	return dataString;
}

char* ReadAFile(const char* dir, const char* filename, uint32& length, bool binary) {
	if (!dir || !filename || strlen(filename) == 0)
		return NULL;

	char* fullname = new char[strlen(dir) + strlen(filename) + 5];
	snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
	char* res = ReadAFile(fullname, length, binary);
	delete [] fullname;
	return res;
}

char* ReadAFile(const char* filename, uint32& length, bool binary) {
	if (!filename || strlen(filename) == 0)
		return NULL;

	length = 0;

	FILE* file;
	if (binary)
		file = fopen(filename, "rb");
	else
		file = fopen(filename, "r");
	if (file == NULL) {
		//LogPrint(0,0,0,"Couldn't open file %s", filename);
		return NULL;
	}

	fseek(file, 0, SEEK_END);
	length = ftell(file);
	fseek(file, 0, SEEK_SET);

	if (length <= 0) {
		fclose(file);
		//LogPrint(0,0,0,"File %s has no size", filename);
		return NULL;
	}

	char* data = new char[length+1];

	int res = (int)fread(data, 1, length, file);

	if ((res <= 0) || (res != length)) {
		int error = ferror(file);
		int eof = feof(file);
		if (eof == 0) {
			delete [] data;
			fclose(file);
			LogPrint(0,0,0,"File %s has no data", filename);
			return NULL;
		}
	}

	fclose(file);

	length = (uint32)res;
	data[length] = 0;
	return data;
}

bool WriteAFile(const char* dir, const char* filename, const char* data, uint32 length, bool binary) {
	if (!dir || !filename || strlen(filename) == 0)
		return false;

	char* fullname = new char[strlen(dir) + strlen(filename) + 5];
	snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
	bool res = WriteAFile(fullname, data, length, binary);
	delete [] fullname;
	return res;
}

bool WriteAFile(const char* filename, const char* data, uint32 length, bool binary) {
	if (!filename || strlen(filename) == 0)
		return false;

	FILE* file;
	if (binary)	
		file = fopen(filename, "wb");
	else
		file = fopen(filename, "w");
	if (file == NULL)
		return false;

	int res = (int)fwrite(data, 1, length, file);
	fclose(file);
	return (res == length);
}

bool AppendToAFile(const char* dir, const char* filename, const char* data, uint32 length, bool binary) {
	if (!dir || !filename || strlen(filename) == 0)
		return false;

	char* fullname = new char[strlen(dir) + strlen(filename) + 5];
	snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
	bool res = AppendToAFile(fullname, data, length, binary);
	delete [] fullname;
	return res;
}

bool AppendToAFile(const char* filename, const char* data, uint32 length, bool binary) {
	if (!filename || strlen(filename) == 0)
		return false;

	FILE* file;
	if (binary)	
		file = fopen(filename, "ab");
	else
		file = fopen(filename, "a");
	if (file == NULL)
		return false;

	int res = (int)fwrite(data, 1, length, file);
	fclose(file);
	return (res == length);
}

bool DeleteAFile(const char* dir, const char* filename, bool force) {
	if (!dir || !filename || strlen(filename) == 0)
		return false;

	char* fullname = new char[strlen(dir) + strlen(filename) + 5];
	snprintf(fullname, strlen(dir) + strlen(filename) + 5, "%s/%s", dir, filename);
	bool res = DeleteADir(fullname, force);
	delete [] fullname;
	return res;
}

bool DeleteAFile(const char* filename, bool force) {
	return DeleteADir(filename, force);
}

bool ChangeAFileAttr(const char* filename, bool read, bool write) {
	if (!filename || strlen(filename) == 0)
		return false;
	#ifdef WINDOWS
		if (!write)
			return (SetFileAttributes(filename, FILE_ATTRIBUTE_READONLY) != 0);
		else
			return (SetFileAttributes(filename, FILE_ATTRIBUTE_NORMAL) != 0);
	#else
		if (read && write)
			return (chmod(filename,S_IREAD | S_IWRITE) != 0);
		else if (read)
			return (chmod(filename,S_IREAD) == 0);
		else if (write)
			return (chmod(filename,S_IWRITE) == 0);
		else
			return (chmod(filename,0) == 0);
	#endif
}

bool MoveAFile(const char* oldfilename, const char* newfilename, bool force) {
	#ifdef WINDOWS
		bool result;
		if (force)
			result = (MoveFileEx(oldfilename, newfilename, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) != 0);
		else
			result = (MoveFileEx(oldfilename, newfilename, MOVEFILE_COPY_ALLOWED) != 0);
		if (result)
			return true;
		LogPrint(0,LOG_SYSTEM,0,"MoveAFile '%s' to '%s' failed with: %s", oldfilename, newfilename, GetLastOSErrorMessage().c_str());
		return false;
	#else
		if (rename(oldfilename, newfilename) == 0)
			return true;
		int error = errno;
		if (force) {
			FileDetails info = GetFileDetails(newfilename);
			if (info.doesExist) {
				delete(newfilename);
				if (rename(oldfilename, newfilename) == 0)
					return true;
			}
		}
		if (error == EXDEV) {
			if (CopyAFile(newfilename, oldfilename)) {
				DeleteAFile(oldfilename, true);
				return true;
			}
		}
		return false;
	#endif
}

bool MoveAFile(const char* olddir, const char* oldfilename, const char* newdir, const char* newfilename, bool force) {
	if (!newdir || !newfilename || !strlen(newfilename) ||
		!olddir || !oldfilename || !strlen(oldfilename) )
		return false;

	char* oldname = new char[strlen(olddir) + strlen(oldfilename) + 5];
	snprintf(oldname, strlen(olddir) + strlen(oldfilename) + 5, "%s/%s", olddir, oldfilename);
	char* newname = new char[strlen(newdir) + strlen(newfilename) + 5];
	snprintf(newname, strlen(newdir) + strlen(newfilename) + 5, "%s/%s", newdir, newfilename);
	bool res = MoveAFile(newname, oldname, force);
	delete [] oldname;
	delete [] newname;
	return res;
}

bool CopyAFile(const char* oldfilename, const char* newfilename, bool force) {
	#ifdef WINDOWS
		if (force)
			return (CopyFile(oldfilename, newfilename, FALSE) != 0);
		else
			return (CopyFile(oldfilename, newfilename, TRUE) != 0);
	#else
		FileDetails target = GetFileDetails(newfilename);
		if (target.doesExist && !force)
			return false;
		if (target.isDirectory)
			return false;
		uint32 size;
		char* data = ReadAFile(oldfilename, size, true);
		if (!data || !size) {
			delete [] data;
			return false;
		}

		if ((target.doesExist) && !DeleteAFile(newfilename, true)) {
			delete [] data;
			return false;
		}

		if (!WriteAFile(newfilename, data, size, true)) {
			delete [] data;
			return false;
		}
		delete [] data;
		return true;
	#endif
}

bool CopyAFile(const char* olddir, const char* oldfilename, const char* newdir, const char* newfilename, bool force) {
	if (!newdir || !newfilename || !strlen(newfilename) ||
		!olddir || !oldfilename || !strlen(oldfilename) )
		return false;

	char* oldname = new char[strlen(olddir) + strlen(oldfilename) + 5];
	snprintf(oldname, strlen(olddir) + strlen(oldfilename) + 5, "%s/%s", olddir, oldfilename);
	char* newname = new char[strlen(newdir) + strlen(newfilename) + 5];
	snprintf(newname, strlen(newdir) + strlen(newfilename) + 5, "%s/%s", newdir, newfilename);
	bool res = CopyAFile(newname, oldname, force);
	delete [] oldname;
	delete [] newname;
	return res;
}

bool CreateADir(const char* dirname) {
	if (!dirname || strlen(dirname) == 0)
		return false;

	FileDetails info = GetFileDetails(dirname);

	if (info.doesExist) {
		if (info.isDirectory)
			return true;
		else
			return false;
	}

	#ifdef WINDOWS
		if (CreateDirectory(dirname, NULL) != 0)
			return true;
		else
			return false;
	#else
		if (mkdir(dirname, S_IRWXU | S_IRWXG | S_IRWXO) == 0)
			return true;
		else
			return false;
	#endif
}

bool DeleteADir(const char* dirname, bool force) {
	if (!dirname || strlen(dirname) == 0)
		return false;

	FileDetails info = GetFileDetails(dirname);

	if (!info.doesExist)
		return true;

	// If it is a file
	if (!info.isDirectory) {
		#ifdef WINDOWS
			if (DeleteFile(dirname) != 0)
				return true;
			else if (!force)
				return false;
			else {
				ChangeAFileAttr(dirname, true, true);
				if (DeleteFile(dirname) != 0)
					return true;
				else
					return false;
			}
		#else
			if (unlink(dirname) == 0)
				return true;
			else if (!force)
				return false;
			else {
				ChangeAFileAttr(dirname, true, true);
				if (unlink(dirname) == 0)
					return true;
				else
					return false;
			}
		#endif
	}

	// Now we know it is a dir
	#ifdef WINDOWS
		if (RemoveDirectory(dirname) != 0)
			return true;
		else if (!force)
			return false;
		else {
			DeleteFilesInADir(dirname, true);
			if (RemoveDirectory(dirname) != 0)
				return true;
			else
				return false;
		}
	#else
		if (rmdir(dirname) == 0)
			return true;
		else if (!force)
			return false;
		else {
			DeleteFilesInADir(dirname, true);
			if (rmdir(dirname) == 0)
				return true;
			else
				return false;
		}
	#endif

}

bool DeleteFilesInADir(const char* dirname, bool force) {
	if (!dirname || strlen(dirname) == 0)
		return false;

	uint32 count;
	char* files = utils::GetFileList(dirname, NULL, count, true);
	if (!files)
		return true;

	const char* name;
	for (uint32 n = 0; n<count; n++) {
		name = files + (n*(MAXFILENAMELEN+1));
		if (!DeleteADir(name, force)) {
			delete[] files;
			return false;
		}
	}

	delete [] files;
	return true;
}





uint32 TextReplaceCharsInPlace(char* str, uint32 size, char find, char replace) {
	uint32 c = 0;
	for (uint32 n=0; n<size; n++) {
		if (str[n] == find) {
			str[n] = replace;
			c++;
		}
	}
	return c;
}

const char* laststrstr(const char* str1, const char* str2) {
	const char* strp;
	int len1, len2;
 
	len2 = (int)strlen(str2);
	if(len2==0)
		return (char*)str1;
 
	len1 = (int)strlen(str1);
	if (len1 == len2)
		return (strcmp(str1, str2) == 0) ? str1 : 0;
	else if (len1 - len2 < 0)
		return 0;
	
	strp = (char*)(str1 + len1 - len2);
	while(strp != str1) {
		if(*strp == *str2) {
			if(strncmp(strp,str2,len2)==0)
				return strp;
		}
		strp--;
	}
	return 0;
}

const char* laststristr(const char* str1, const char* str2) {
	const char* strp;
	int len1, len2;
 
	len2 = (int)strlen(str2);
	if(len2==0)
		return (char*)str1;
 
	len1 = (int)strlen(str1);
	if (len1 == len2)
		return (stricmp(str1, str2) == 0) ? str1 : 0;
	else if (len1 - len2 < 0)
		return 0;
	
	strp = (char*)(str1 + len1 - len2);
	while(strp != str1) {
		if(*strp == *str2) {
			if(strnicmp(strp,str2,len2)==0)
				return strp;
		}
		strp--;
	}
	return 0;
}

bool TextEndsWith(const char* str, const char* end, bool caseSensitive) {
	if (!caseSensitive)
		return (laststristr(str, end) == str + strlen(str) - strlen(end));
	else
		return (laststrstr(str, end) == str + strlen(str) - strlen(end));
}

bool TextStartsWith(const char* str, const char* start, bool caseSensitive) {
	if (!caseSensitive)
		return (stristr(str, start) == str);
	else
		return (strstr(str, start) == str);
}


std::string GetFilePath(const char* filename) {
	std::string name = filename;
	std::string::size_type p1 = name.find_last_of('/');
	std::string::size_type p2 = name.find_last_of('\\');
	if (p1 == std::string::npos) {
		if (p2 == std::string::npos)
			return "";
		else
			return name.substr(0, p2 + 1);
	}
	else if (p2 == std::string::npos)
		return name.substr(0, p1 + 1);
	else
		return name.substr(0, (p1 > p2) ? p1 + 1 : p2 + 1);
}

const char* GetFileBasename(const char* filename) {
	std::string name = filename;
	std::string::size_type p1 = name.find_last_of('/');
	std::string::size_type p2 = name.find_last_of('\\');
	if (p1 == std::string::npos) {
		if (p2 == std::string::npos)
			return filename;
		else
			return filename + p2 + 1;
	}
	else if (p2 == std::string::npos)
		return filename + p1 + 1;
	else
		return (p1 > p2) ? filename + p1 + 1 : filename + p2 + 1;
}

std::string GetCurrentDir() {

	char cCurrentPath[FILENAME_MAX];

	if (!GetCurrentDirEx(cCurrentPath, sizeof(cCurrentPath)))
	{
		return "";
	}
	cCurrentPath[sizeof(cCurrentPath) - 1] = '\0'; /* not really required */
	//printf("The current working directory is %s\n", cCurrentPath);
	return cCurrentPath;
}

char* GetFileList(const char* dirname, const char* ext, uint32& count, bool fullpath, uint32 maxNameLen) {
	DIR* dir = opendir(dirname);
	if (dir == NULL)
		return NULL;

	uint32 extlen = (ext == NULL) ? 0 : (uint32)strlen(ext);
	struct dirent* direntry = NULL;

	uint32 tempMaxCount = 50;
	uint32 len = (maxNameLen+1)*tempMaxCount;
	char* result = new char[len];
	char* dst = result;
	char* tmp;
	uint32 dirnamelen = (uint32)strlen(dirname);
	bool dirslash = (*(dirname + dirnamelen - 1) == '/') || (*(dirname + dirnamelen - 1) == '\\');

	count = 0;
	direntry = readdir(dir);
	while (direntry) {
	#ifdef WINDOWS
		if (direntry->d_name && direntry->d_name[0] != '\0') {
	#else
		if (direntry->d_name[0] != '\0') {
	#endif
			if ( !strcmp(direntry->d_name, ".") || !strcmp(direntry->d_name, "..") ) {
				direntry = readdir(dir);
				continue;
			}
			if (ext && extlen && !TextEndsWith(direntry->d_name, ext)) {
				direntry = readdir(dir);
				continue;
			}
			if (fullpath) {
				utils::strcpyavail(dst, dirname, maxNameLen, true);
				if (!dirslash) {
					memset(dst+dirnamelen, '/', 1);
					utils::strcpyavail(dst+dirnamelen+1, direntry->d_name, maxNameLen, true);
				}
				else
					utils::strcpyavail(dst+dirnamelen, direntry->d_name, maxNameLen, true);
			}
			else
				utils::strcpyavail(dst, direntry->d_name, maxNameLen, true);
			dst+= maxNameLen+1;
			count++;
			if (count >= tempMaxCount) {
				tempMaxCount *= 2;
				tmp = new char[len*2];
				memcpy(tmp, result, len);
				delete [] result;
				result = tmp;
				dst = result + len;
				len *= 2;
			}
		}
		direntry = readdir(dir);
	}
	closedir(dir);
	return result;
}

bool DoesADirExist(const char* dirname) {
	FileDetails details = GetFileDetails(dirname);
	return (details.doesExist && details.isDirectory);
}

bool DoesAFileExist(const char* filename) {
	FileDetails details = GetFileDetails(filename);
	return (details.doesExist && !details.isDirectory);
}

FileDetails GetFileDetails(const char* filename) {
	struct stat statbuf;
	int fd, result;

	FileDetails info;

	info.doesExist = false;
	info.isDirectory = false;
	info.isReadable = false;
	info.isWritable = false;
	info.isExecutable = false;
	info.size = 0;
	info.lastAccessTime = 0;
	info.lastModifyTime = 0;
	info.creationTime = 0;

	// If file cannot be opened
	if ((fd = _open(filename, O_RDONLY)) ==  -1) {
		// On Windows, this might be a dir
		DIR* dir = opendir(filename);
		if (dir == NULL)
			return info;
		struct dirent* direntry = readdir(dir);

		info.doesExist = true;
		info.isDirectory = true;
		info.lastAccessTime = info.lastModifyTime = info.creationTime = GetTimeNow();
		info.isReadable = true;
		info.isWritable = true;
		info.isExecutable = true;

		closedir(dir);
		return info;
	}

	// Get data associated with "fd":
	result = fstat(fd, &statbuf);

	// Check if statistics are valid:
	if( result != 0 ) {
		_close(fd);
		return info;
	}

	_close(fd);
	info.doesExist = true;

	info.size = statbuf.st_size;
	info.isDirectory = ((statbuf.st_mode & S_IFDIR) != 0);
	info.lastAccessTime = FTime2PsyTime(statbuf.st_atime);
	info.lastModifyTime = FTime2PsyTime(statbuf.st_ctime);
	info.creationTime = FTime2PsyTime(statbuf.st_ctime);

	#ifdef WINDOWS
		info.isReadable = ((statbuf.st_mode & _S_IREAD) != 0);
		info.isWritable = ((statbuf.st_mode & _S_IWRITE) != 0);
		info.isExecutable = ((statbuf.st_mode & _S_IEXEC) != 0);
	#else
		info.isReadable = ((statbuf.st_mode & S_IRUSR) != 0);
		info.isWritable = ((statbuf.st_mode & S_IWUSR) != 0);
		info.isExecutable = ((statbuf.st_mode & S_IXUSR) != 0);
	#endif

	return info;

}

char* TextSubstringCopy(const char* ascii, uint32 start, uint32 end) {
	uint32 len;
	char* copy;
	if (!ascii || (start >= end) || (!(len = (uint32)strlen(ascii))) ) {
		copy = new char[1];
		copy[0] = 0;
	}
	else {
		if (end > len - 1)
			end = len - 1;
		copy = new char[end - start + 2];
		memcpy(copy, ascii + start,
			end - start + 1);
		copy[end - start + 1] = 0;
	}
	return copy;
}


bool IsTextNumeric(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	uint32 len = (uint32)strlen(ascii);
	uint32 stop = end ? end+1 : len;
	if (stop > len) stop = len;
	for (uint32 n=start; n<stop; n++) {
        if (( ascii[n] < '0' || ascii[n] > '9') && ascii[n] != '.')
            return false;
	}
	return true;
}

int64 Ascii2Int64(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
	#ifdef WINDOWS
		return _strtoi64(ascii + start, NULL, 10);
	#else
		return strtoll(ascii + start, NULL, 10);
	#endif
	char* copy = TextSubstringCopy(ascii, start, end);
	#ifdef WINDOWS
		int64 val = _strtoi64(copy, NULL, 10);
	#else
		int64 val = strtoll(copy, NULL, 10);
	#endif
	delete[] copy;
	return val;
}

uint64 Ascii2Uint64(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
	#ifdef WINDOWS
		return _strtoui64(ascii + start, NULL, 10);
	#else
		return strtoull(ascii + start, NULL, 10);
	#endif
	char* copy = TextSubstringCopy(ascii, start, end);
	#ifdef WINDOWS
		uint64 val = _strtoui64(copy, NULL, 10);
	#else
		uint64 val = strtoull(copy, NULL, 10);
	#endif
	delete[] copy;
	return val;
}

uint64 AsciiHex2Uint64(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
	#ifdef WINDOWS
		return _strtoui64(ascii + start, NULL, 16);
	#else
		return strtoull(ascii + start, NULL, 16);
	#endif
	char* copy = TextSubstringCopy(ascii, start, end);
	#ifdef WINDOWS
		uint64 val = _strtoui64(copy, NULL, 16);
	#else
		uint64 val = strtoull(copy, NULL, 16);
	#endif
	delete[] copy;
	return val;
}

int32 Ascii2Int32(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
		return (int32)strtol(ascii + start, NULL, 10);
	char* copy = TextSubstringCopy(ascii, start, end);
	int32 val = (int32)strtol(copy, NULL, 10);
	delete[] copy;
	return val;
}

uint32 Ascii2Uint32(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
		return (uint32)strtoul(ascii + start, NULL, 10);
	char* copy = TextSubstringCopy(ascii, start, end);
	uint32 val = (uint32)strtoul(copy, NULL, 10);
	delete[] copy;
	return val;
}

uint32 AsciiHex2Uint32(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
		return (uint32)strtoul(ascii + start, NULL, 16);
	char* copy = TextSubstringCopy(ascii, start, end);
	uint32 val = (uint32)strtoul(copy, NULL, 16);
	delete[] copy;
	return val;
}

float64 Ascii2Float64(const char* ascii, uint32 start, uint32 end) {
	if (!ascii || (start > end)) return false;
	if (!end)
		return strtod(ascii + start, NULL);
	char* copy = TextSubstringCopy(ascii, start, end);
	float64 val = strtod(copy, NULL);
	delete[] copy;
	return val;
}


std::string DecodeJSON(std::string str) {
	bool isEscaped = false;
	char* tmp = new char[5];
	tmp[4] = 0;
	std::ostringstream o;
	std::string::iterator c;
    for (c = str.begin(); c != str.end(); c++) {
		if (isEscaped) {
			switch (*c) {
			case '"': o << "\""; break;
			case '\\': o << "\\"; break;
			case 'b': o << "\b"; break;
			case 'f': o << "\f"; break;
			case 'n': o << "\n"; break;
			case 'r': o << "\r"; break;
			case 't': o << "\t"; break;
			case 'u':
				c++;
				tmp[0] = (uint8)(*c++);
				tmp[1] = (uint8)(*c++);
				tmp[2] = (uint8)(*c++);
				tmp[3] = (uint8)(*c);
				o << (char) strtol(tmp, NULL, 16);
				break;
			default: o << *c;
			}
			isEscaped = false;
		}
		else {
			switch (*c) {
			case '\\': isEscaped = true; break;
			default: o << *c;
			}
		}
    }
	delete[] tmp;
    return o.str();
}

std::string EncodeJSON(std::string str) {
    std::ostringstream o;
	std::string::iterator c;
	for (c = str.begin(); c != str.end(); c++) {
        switch (*c) {
        case '"': o << "\\\""; break;
        case '\\': o << "\\\\"; break;
        case '\b': o << "\\b"; break;
        case '\f': o << "\\f"; break;
        case '\n': o << "\\n"; break;
        case '\r': o << "\\r"; break;
        case '\t': o << "\\t"; break;
        default:
            if ('\x00' <= *c && *c <= '\x1f') {
                o << "\\u"
                  << std::hex << std::setw(4) << std::setfill('0') << (int)*c;
            } else {
                o << *c;
            }
        }
    }
    return o.str();

	//if (str.size() == 0)
	//	return str;
	//std::string result = str;
	//char c;
	//char* tmp = (char*)malloc(20);

	//for (uint32 n=0; n<result.size(); n++) {
	//	switch(c = result[n]) {
	//		case '\n':
	//			result.replace(n, 1, "\\n");
	//			n+=1;
	//			break;
	//		case '\r':
	//			result.replace(n, 1, "\\r");
	//			n+=1;
	//			break;
	//		case '\t':
	//			result.replace(n, 1, "\\t");
	//			n+=2;
	//			break;
	//		case '"':
	//			result.replace(n, 1, "\\\"");
	//			n+=2;
	//			break;
	//		case '\\':
	//			result.replace(n, 1, "\\\\");
	//			n+=2;
	//			break;
	//		case '\b':
	//			result.replace(n, 1, "\\b");
	//			n+=2;
	//			break;
	//		case '\f':
	//			result.replace(n, 1, "\\f");
	//			n+=2;
	//			break;
	//		default:
	//			if ('\x00' <= c && c <= '\x1f') {
	//				snprintf(tmp, 20, "\\u%.4x;", (int) c & 0xFF);
	//				result.replace(n, 1, tmp);
	//				n+=(uint32)strlen(tmp)-1;
	//			}
	//			break;
	//	}
	//}

	//free((char*)tmp);

	//return result;
}


// Seeds the rand() function
bool SeedRandomValues(uint32 seedvalue) {
	srand( seedvalue ? seedvalue : (uint32)GetTimeNow());
	return true;
}

// Returns a random value between 0.0 and 1.0
double RandomValue() {
	double d = 0;
	double t = (1.0/((RAND_MAX + 1.0)*(RAND_MAX + 1.0)*(RAND_MAX + 1.0)));
	do {
		d = (rand () * ((RAND_MAX + 1.0) * (RAND_MAX + 1.0))
			+ rand () * (RAND_MAX + 1.0)
			+ rand ()) * t;
	} while (d >= 1); /* Round off */
	return d;
}

// Returns a random value between 0.0 and max
double RandomValue(double max) {
	return RandomValue() * max;
}

// Returns a random value between from and to
double RandomValue(double from, double to) {
	if (to < from)
		return (RandomValue()*(from-to))+to;
	else
		return (RandomValue()*(to-from))+from;
}

// Returns a random value between from and to, rounded to nearest interval
double RandomValue(double from, double to, double interval) {
	if (to < from)
		return RandomValue(to, from, interval);
	double r = RandomValue(0, to-from);
	uint64 n = (uint64)(r/interval);
	double l = r - (n*interval);
	if (l >= (0.5*interval))
		return from + (n*interval);
	else
		return from + ((n+1)*interval);
}

int64 RandomInt(int64 from, int64 to) {
	return (int64)roundl(RandomValue((double)from, (double)to));
}


//std::string BytifySize(int32 val) {
//	return BytifySize((double)val);
//}

std::string BytifySize(double val) {
	
	double kb = 1024;
	double mb = kb*1024;
	double gb = mb*1024;
	double tb = gb*1024;

	char *tmp = new char[64];

	if (val < 512)
		snprintf(tmp, 64, "%.2f B", val);
	else if (val < 512*kb) {
		val = val / kb;
		snprintf(tmp, 64, "%.2f KB", val);
	}
	else if (val < 512*mb) {
		val = val / mb;
		snprintf(tmp, 64, "%.2f MB", val);
	}
	else if (val < 512*gb) {
		val = val / gb;
		snprintf(tmp, 64, "%.2f GB", val);
	}
	else {
		val = val / tb;
		snprintf(tmp, 64, "%.2f TB", val);
	}

	std::string result = tmp;
	delete [] tmp;
	return result;
}

//std::string BytifySizes(int32 val1, int32 val2) {
//	return BytifySizes((double)val1, (double) val2);
//}

std::string BytifySizes(double val1, double val2) {
	
	double kb = 1024;
	double mb = kb*1024;
	double gb = mb*1024;
	double tb = gb*1024;

	char *tmp = new char[64];
	char *end = new char[64];

	double val;
	double maximum = ((val1 > val2) ? val1 : val2);

	if (maximum < 512) {
		val = 1;
		utils::strcpyavail(end, "B", 32, true);
	}
	else if (maximum < 512*kb) {
		val = kb;
		utils::strcpyavail(end, "KB", 32, true);
	}
	else if (maximum < 512*mb) {
		val = mb;
		utils::strcpyavail(end, "MB", 32, true);
	}
	else if (maximum < 512*gb) {
		val = gb;
		utils::strcpyavail(end, "GB", 32, true);
	}
	else {
		val = gb;
		utils::strcpyavail(end, "TB", 32, true);
	}
	snprintf(tmp, 64, "%.2f / %.2f %s", ((double)val1)/val, ((double)val2)/val, (char*) end);

	std::string result = tmp;
	delete [] tmp;
	delete [] end;
	return result;

}

//std::string BytifyRate(int32 val) {
//	return BytifyRate((double)val);
//}

std::string BytifyRate(double val) {
	char *tmp = new char[64];
	snprintf(tmp, 64, "%s/sec", (char*) BytifySize(val).c_str());
	std::string result = tmp;
	delete [] tmp;
	return result;
}

//std::string BytifyRates(int32 val1, int32 val2) {
//	return BytifyRates((double)val1, (double)val2);
//}

std::string BytifyRates(double val1, double val2) {
	char *tmp = new char[64];
	snprintf(tmp, 64, "%s/sec", (char*) BytifySizes(val1, val2).c_str());
	std::string result = tmp;
	delete [] tmp;
	return result;
}







#ifdef WINDOWS

uint32 ReadRegistryDWORD(const char* key, const char* entry) {
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS)
		return 0;

	DWORD dwType = REG_DWORD;
	DWORD numVal = 0;
	DWORD dwSize = sizeof(numVal);

	err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)&numVal, &dwSize);
	RegCloseKey(hKey);
	if (err != ERROR_SUCCESS)
		return 0;
	return numVal;
}

uint64 ReadRegistryQWORD(const char* key, const char* entry) {
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS)
		return 0;

	DWORD dwType = REG_QWORD;
	uint64 numVal = 0;
	DWORD dwSize = sizeof(numVal);

	err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)&numVal, &dwSize);
	RegCloseKey(hKey);
	if (err != ERROR_SUCCESS)
		return 0;
	return numVal;
}

std::string ReadRegistryString(const char* key, const char* entry) {
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS) {
		return "";
	}

	DWORD dwType = REG_SZ;
	DWORD dwSize = 4096;
	char* lszValue = new char[dwSize];

	err = RegQueryValueEx(hKey, entry, NULL, &dwType, (LPBYTE)lszValue, &dwSize);
	RegCloseKey(hKey);
	if (err != ERROR_SUCCESS) {
		delete[] lszValue;
		return "";
	}

	std::string val = lszValue;
	delete[] lszValue;
	return val;
}

bool WriteRegistryDWORD(const char* key, const char* entry, uint32 value) {
	DWORD dwDisposition;
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS) {
		err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
		if (err != ERROR_SUCCESS)
			return false;
	}

	DWORD dwType = REG_DWORD;
	err = RegSetValueEx(hKey, entry, 0, dwType, (const BYTE*)&value, sizeof(value));
	RegCloseKey(hKey);
	return (err == ERROR_SUCCESS);
}

bool WriteRegistryQWORD(const char* key, const char* entry, uint64 value) {
	DWORD dwDisposition;
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS) {
		err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
		if (err != ERROR_SUCCESS)
			return false;
	}

	DWORD dwType = REG_QWORD;
	err = RegSetValueEx(hKey, entry, 0, dwType, (const BYTE*)&value, sizeof(value));
	RegCloseKey(hKey);
	return (err == ERROR_SUCCESS);
}

bool WriteRegistryString(const char* key, const char* entry, const char* value) {
	DWORD dwDisposition;
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_WRITE | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS) {
		err = RegCreateKeyEx(hKeyRoot, keySubName.c_str(), 0, NULL, 0, KEY_WRITE | KEY_WOW64_64KEY, NULL, &hKey, &dwDisposition);
		if (err != ERROR_SUCCESS)
			return false;
	}

	DWORD dwType = REG_SZ;
	DWORD dwSize = 4096;

	err = RegSetValueEx(hKey, entry, 0, dwType, (LPBYTE)value, (DWORD)(strlen(value) + 1));
	RegCloseKey(hKey);
	return (err == ERROR_SUCCESS);
}

bool DeleteRegistryEntry(const char* key, const char* entry) {
	HKEY hKeyRoot, hKey;
	std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);

	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_SET_VALUE | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS)
		return false;

	//std::string keySubName = GetRegistryKeyInfo(key, hKeyRoot);
	err = RegDeleteValue(hKey, entry);
	return (err == ERROR_SUCCESS);
}

bool DeleteRegistryKey(const char* entry) {
	// https://msdn.microsoft.com/en-us/library/aa379776(VS.85).aspx

	HKEY hKeyRoot;
	std::string keySubName = GetRegistryKeyInfo(entry, hKeyRoot);
	uint32 err = RegDeleteKeyEx(hKeyRoot, keySubName.c_str(), KEY_WOW64_64KEY, 0);
	return (err == ERROR_SUCCESS);
}

bool DeleteRegistryTree(const char* root, const char* key) {
	if (!key || !strlen(key))
		return false;

	HKEY hKey;
	HKEY hKeyRoot;
	std::string keySubName = GetRegistryKeyInfo(root, hKeyRoot);
	uint32 err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, DELETE | KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS)
		return false;

	err = RegDeleteTree(hKey, key);
	return (err == ERROR_SUCCESS);
}

//bool CopyRegistryKey(const char* key, const char* newName) {
//}

bool CopyRegistryTree(const char* entry, const char* newName) {
	// https://msdn.microsoft.com/en-us/library/aa379768(VS.85).aspx

	// access 64 or 32-bit registry: https://msdn.microsoft.com/en-us/library/aa384129(v=vs.85).aspx
	// we always use the 64-bit registry here

	DWORD dwDisposition;
	HKEY hKey;
	HKEY hKeyRoot;
	HKEY hKeyNew;
	HKEY hKeyNewRoot;
	uint32 err;

	std::string keySubName = GetRegistryKeyInfo(entry, hKeyRoot);
	std::string keySubNewName = GetRegistryKeyInfo(newName, hKeyNewRoot);

	err = RegOpenKeyEx(hKeyRoot, keySubName.c_str(), 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
	if (err != ERROR_SUCCESS)
		return false;

	err = RegCreateKeyEx(hKeyNewRoot, keySubNewName.c_str(), 0, NULL, 0, KEY_ALL_ACCESS | KEY_WOW64_64KEY, NULL, &hKeyNew, &dwDisposition);
	if (err != ERROR_SUCCESS) {
		RegCloseKey(hKey);
		return false;
	}

	err = RegCopyTree(hKey, NULL, hKeyNew);

	RegCloseKey(hKey);
	RegCloseKey(hKeyNew);

	if (err != ERROR_SUCCESS) {
		RegDeleteKeyEx(hKeyNewRoot, keySubNewName.c_str(), KEY_WOW64_64KEY, 0);
		return false;
	}
	return true;
}

//bool RenameRegistryKey(const char* key, const char* newName) {
//	if (!CopyRegistryKey(key, newName))
//		return false;
//	if (!DeleteRegistryKey(key)) {
//		CopyRegistryKey(newName, key);
//		return false;
//	}
//	return true;
//}

//bool RenameRegistryTree(const char* entry, const char* newName) {
	//if (!CopyRegistryTree(entry, newName))
	//	return false;
	//if (!DeleteRegistryTree(entry)) {
	//	CopyRegistryTree(newName, entry);
	//	return false;
	//}
//	return true;
//}

std::string GetRegistryKeyInfo(const char* key, HKEY &hKeyRoot) {
	std::vector<std::string> path = utils::TextListSplit(key, "\\");
	if (!path.size()) path = utils::TextListSplit(key, "/");
	if (path.size() < 2) return false;
	if (stristr(path.at(0).c_str(), "HKEY_CLASSES_ROOT"))
		hKeyRoot = HKEY_CLASSES_ROOT;
	else if (stristr(path.at(0).c_str(), "HKEY_CURRENT_USER"))
		hKeyRoot = HKEY_CURRENT_USER;
	else if (stristr(path.at(0).c_str(), "HKEY_LOCAL_MACHINE"))
		hKeyRoot = HKEY_LOCAL_MACHINE;
	else if (stristr(path.at(0).c_str(), "HKEY_USERS"))
		hKeyRoot = HKEY_USERS;
	return utils::TextJoin(path, "\\", 1, (DWORD)(path.size() - 1));
}

std::string GetRegistryParentKeyInfo(const char* key, HKEY &hKeyRoot) {
	std::vector<std::string> path = utils::TextListSplit(key, "\\");
	if (!path.size()) path = utils::TextListSplit(key, "/");
	if (path.size() < 2) return false;
	if (stristr(path.at(0).c_str(), "HKEY_CLASSES_ROOT"))
		hKeyRoot = HKEY_CLASSES_ROOT;
	else if (stristr(path.at(0).c_str(), "HKEY_CURRENT_USER"))
		hKeyRoot = HKEY_CURRENT_USER;
	else if (stristr(path.at(0).c_str(), "HKEY_LOCAL_MACHINE"))
		hKeyRoot = HKEY_LOCAL_MACHINE;
	else if (stristr(path.at(0).c_str(), "HKEY_USERS"))
		hKeyRoot = HKEY_USERS;
	return utils::TextJoin(path, "\\", 1, (DWORD)(path.size() - 2));
}


#else // WINDOWS
	int32 ReadRegistryDWORD(const char* key) { return 0; }
	int64 ReadRegistryQWORD(const char* key) { return 0; }
	std::string ReadRegistryString(const char* key) { return ""; }
	bool WriteRegistryDWORD(const char* key, int32 value) { return false; }
	bool WriteRegistryQWORD(const char* key, int64 value) { return false; }
	bool WriteRegistryString(const char* key, const char* value) { return false; }
	bool DeleteRegistryKey(const char* key) { return false; }
	bool DeleteRegistryTree(const char* key) { return false; }
	//bool CopyRegistryKey(const char* key, const char* newName) { return false; }
	bool CopyRegistryTree(const char* entry, const char* newName) { return false; }
	//bool RenameRegistryKey(const char* key, const char* newName) { return false; }
	bool RenameRegistryTree(const char* entry, const char* newName) { return false; }
#endif // WINDOWS



} // namespace utils

// Adapters so the utils:: free-function tests can be registered as bool() pointers.
static bool Test_Utils() { return utils::UnitTest_Utils(); }
static bool Test_Timer() { return utils::UnitTest_Timer(); }

void Register_Utils_Tests() {
	UnitTestRunner::instance().registerTest("utils", Test_Utils,
		"Bitfield slot allocation: SetBit/SetBitN, first-free and last-occupied searches", "util");
	UnitTestRunner::instance().registerTest("timer", Test_Timer,
		"Timer scheduling and trigger delivery via waitForTimer", "util");
}

} // namespace cmlabs


