You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

835 lines
25 KiB

////////////////////////////////////////////////////////////////////
// Util.h
//
// Copyright 2007 cDc@seacave
// Distributed under the Boost Software License, Version 1.0
// (See http://www.boost.org/LICENSE_1_0.txt)
#ifndef __SEACAVE_UTIL_H__
#define __SEACAVE_UTIL_H__
// I N C L U D E S /////////////////////////////////////////////////
#ifdef _MSC_VER
#include <io.h>
#else
#include <unistd.h>
#include <dirent.h>
#endif
// D E F I N E S ///////////////////////////////////////////////////
#define PATH_SEPARATOR _T('/')
#define PATH_SEPARATOR_STR _T("/")
#define REVERSE_PATH_SEPARATOR _T('\\')
#ifdef _MSC_VER
#define LINE_SEPARATOR_STR _T("\r\n")
#define LINE_SEPARATOR_LEN 2
#else
#define LINE_SEPARATOR_STR _T("\n")
#define LINE_SEPARATOR_LEN 1
#endif
#ifdef _MSC_VER
#define SETTINGS_PATH _T("%APPDATA%")
#else
#define SETTINGS_PATH _T("~/.config")
#endif
#define ensureUnifySlashWin ensureUnifyReverseSlash
#define ensureUnifySlashUnix ensureUnifySlash
#define GET_TICK() Util::getTick()
#define GET_TIME() Util::getTime()
#define SIMD_ENABLED Util::ms_CPUFNC
namespace SEACAVE {
// S T R U C T S ///////////////////////////////////////////////////
// Manage setting/removing bit flags
template <typename TYPE>
class TFlags
{
public:
typedef TYPE Type;
public:
inline TFlags() : flags(0) { }
inline TFlags(const TFlags& rhs) : flags(rhs.flags) { }
inline TFlags(Type f) : flags(f) { }
inline bool isSet(Type aFlag) const { return (flags & aFlag) == aFlag; }
inline bool isSet(Type aFlag, Type nF) const { return (flags & (aFlag|nF)) == aFlag; }
inline bool isSetExclusive(Type aFlag) const { return flags == aFlag; }
inline bool isAnySet(Type aFlag) const { return (flags & aFlag) != 0; }
inline bool isAnySet(Type aFlag, Type nF) const { const Type m(flags & (aFlag|nF)); return m != 0 && (m & nF) == 0; }
inline bool isAnySetExclusive(Type aFlag) const { return (flags & aFlag) != 0 && (flags & ~aFlag) == 0; }
inline void set(Type aFlag, bool bSet) { if (bSet) set(aFlag); else unset(aFlag); }
inline void set(Type aFlag) { flags |= aFlag; }
inline void unset(Type aFlag) { flags &= ~aFlag; }
inline void flip(Type aFlag) { flags ^= aFlag; }
inline void operator=(TFlags rhs) { flags = rhs.flags; }
inline operator Type() const { return flags; }
inline operator Type&() { return flags; }
protected:
Type flags;
#ifdef _USE_BOOST
// implement BOOST serialization
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive& ar, const unsigned int /*version*/) {
ar & flags;
}
#endif
};
typedef class GENERAL_API TFlags<uint32_t> Flags;
/*----------------------------------------------------------------*/
// A histogram class, that computes the distribution function (df)
// of unique sended value or iterable data inside the provided range.
// The Histogram object can keep a tally of values within a range,
// the range is arranged into some number of bins specified during
// construction.
// Jansson Consulting
// 2009-06-30, updated 2011-06-17 and 2011-08-03
// 2011-12-17 Modified by Pierre Moulon
// - use vector array to avoid memory management
// - add value by sequence with iterator
// 2015-04-04 Modified by cDc
// - rewrite
// - add GetApproximatePermille()
// Dedicated to the public domain.
template <typename TYPE>
class THistogram
{
public:
typedef TYPE Type;
public:
// Construct a histogram that can count within a range of values.
// All bins of the histogram are set to zero.
THistogram(const std::pair<Type,Type>& range, size_t bins=10) :
Start(range.first),
End(range.second),
BinInterval(Type(bins)/(End-Start)),
Freq(bins, 0),
Overflow(0),
Underflow(0) {}
// Construct a histogram from a sequence of data
template <typename DataInputIterator>
void Add(DataInputIterator begin, DataInputIterator end) {
for (DataInputIterator iter = begin; iter != end; ++iter)
Add(static_cast<Type>(*iter));
}
// Increase the count for the bin that holds a value that is in range
// for this histogram or the under-/overflow count if it is not in range
void Add(const Type& x) {
if (x < Start) {
++Underflow;
} else {
const size_t i(static_cast<size_t>((x-Start)*BinInterval));
if (i < Freq.size()) ++Freq[i];
else ++Overflow;
}
}
// Get the sum of all counts in the histogram
inline size_t GetTotalCount() const { return std::accumulate(Freq.begin(), Freq.end(), 0); }
// Get the overflow count
inline size_t GetOverflow() const { return Overflow; }
// Get the underflow count
inline size_t GetUnderflow() const { return Underflow; }
// Get frequencies
inline const std::vector<size_t>& GetHist() const { return Freq; }
// Get XbinsValue
std::vector<Type> GetXbinsValue() const {
const size_t NBins(Freq.size());
std::vector<Type> vec_XbinValue(NBins);
const Type val((End-Start)/static_cast<Type>(NBins-1));
for (size_t i = 0; i < NBins; ++i)
vec_XbinValue[i] = (val*static_cast<Type>(i) + Start);
return vec_XbinValue;
}
// Get start
inline Type GetStart() const { return Start; }
// Get End
inline Type GetEnd() const { return End; }
// Returns the approximate permille
Type GetApproximatePermille(float permille) const {
ASSERT(permille >= 0.f && permille <= 1.f);
size_t NumValues(0);
for (size_t n: Freq)
NumValues += n;
size_t Num(0);
Type UpperBound(Start);
for (size_t i = 0; i < Freq.size(); ++i) {
if (static_cast<float>(Num)/NumValues > permille)
return UpperBound;
Num += Freq[i];
UpperBound = (static_cast<Type>(i)*End)/(Freq.size()-1)+Start;
}
return End;
}
// Text display of the histogram
std::string ToString(const std::string& sTitle = "") const {
std::ostringstream os;
os.precision(3);
os << sTitle << "\n";
const size_t n(Freq.size());
for (size_t i = 0; i < n; ++i)
os << static_cast<float>(End-Start)/n*static_cast<float>(i) << "\t|\t" << Freq[i] << "\n";
os << End << "\n";
return os.str();
}
protected:
const Type Start, End, BinInterval; // min/max/step of values
std::vector<size_t> Freq; // histogram
size_t Overflow, Underflow; // count under/over flow
};
typedef class GENERAL_API THistogram<float> Histogram32F;
typedef class GENERAL_API THistogram<double> Histogram64F;
/*----------------------------------------------------------------*/
class GENERAL_API Util
{
public:
static String getAppName() {
#ifdef _MSC_VER
String buf(MAX_PATH+1, '\0');
GetModuleFileName(NULL, &buf.front(), MAX_PATH);
return ensureUnifySlash(buf);
#else // _MSC_VER
LPTSTR home = getenv("HOME");
if (home == NULL)
return String();
String name(String(home) + "/app");
return ensureUnifySlash(name);
#endif // _MSC_VER
}
// generate a unique name based on process ID and time
static String getUniqueName(TCHAR dash='-')
{
TCHAR szDate[256];
#ifdef _MSC_VER
SYSTEMTIME st;
GetLocalTime(&st);
LPTSTR szTime = szDate+
GetDateFormat(LOCALE_USER_DEFAULT,0,&st,_T("yy''MM''dd"),szDate,80);
GetTimeFormat(LOCALE_USER_DEFAULT,0,&st,_T("HH''mm''ss"),szTime,80);
#else // _MSC_VER
const time_t t = time(NULL);
const struct tm *tmp = localtime(&t);
LPTSTR szTime = szDate+1+
strftime(szDate, 80, "%y%m%d", tmp);
strftime(szTime, 80, "%H%M%S", tmp);
#endif // _MSC_VER
const uint32_t ID((uint32_t(__PROCESS__) + RAND())&0x00FFFFFF);
if (dash)
return String::FormatString("%s%c%s%c%06X", szDate, dash, szTime, dash, ID);
return String::FormatString("%s%s%06X", szDate, szTime, ID);
}
static String translateError(int aError) {
#ifdef _MSC_VER
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
aError,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
String tmp((LPCTSTR)lpMsgBuf);
// Free the buffer.
LocalFree(lpMsgBuf);
String::size_type i;
while ((i = tmp.find_last_of(LINE_SEPARATOR_STR)) != String::npos)
tmp.erase(i, LINE_SEPARATOR_LEN);
return tmp;
#else // _MSC_VER
return strerror(aError);
#endif // _MSC_VER
}
static String& trimUnifySlash(String& path)
{
String::size_type start = 1;
while ((start = path.find(PATH_SEPARATOR, start)) != String::npos)
if (path[start-1] == PATH_SEPARATOR)
path.erase(start, 1);
else
++start;
return path;
}
static String& ensureUnifySlash(String& path)
{
String::size_type start = 0;
while ((start = path.find(REVERSE_PATH_SEPARATOR, start)) != String::npos)
path[start] = PATH_SEPARATOR;
return trimUnifySlash(path);
}
static String& ensureUnifyReverseSlash(String& path)
{
String::size_type start = 0;
while ((start = path.find(PATH_SEPARATOR, start)) != String::npos)
path[start] = REVERSE_PATH_SEPARATOR;
return path;
}
static String& ensureFolderSlash(String& path)
{
if (path.empty())
return path;
String::size_type nEnd = path.size()-1;
if (path[nEnd] != PATH_SEPARATOR)
path += PATH_SEPARATOR;
return path;
}
static void ensureFolder(const String& path)
{
String::size_type start = 0;
while ((start = path.find(PATH_SEPARATOR, start)) != String::npos)
#ifdef _MSC_VER
CreateDirectory(path.substr(0, ++start).c_str(), NULL);
#else
mkdir(path.substr(0, ++start).c_str(), 0755);
#endif
}
static String& ensureValidPath(String& path) {
return simplifyPath(ensureUnifySlash(strTrim(path, _T("\""))));
}
static String& ensureValidFolderPath(String& path) {
return simplifyPath(ensureFolderSlash(ensureUnifySlash(strTrim(path, _T("\"")))));
}
static inline bool isFullPath(LPCTSTR path) {
// returns true if local drive full path or network path
return (path && (
#ifdef _MSC_VER
(path[1]==_T(':') && path[0]!=_T('\0')) ||
#else // _MSC_VER
path[0]==_T('/') ||
#endif // _MSC_VER
#ifdef UNICODE
*reinterpret_cast<const DWORD*>(path)==0x5C005C00/*"\\\\"*/));
#else
*reinterpret_cast<const WORD*>(path)==0x5C5C/*"\\\\"*/));
#endif // UNICODE
}
static String getFullPath(const String& str) {
if (isFullPath(str))
return str;
return getCurrentFolder()+str;
}
static inline bool isParentFolder(LPCTSTR path, int off=0) {
// returns true if the folder starting at the given position in path is the parent folder ".."
if (off < 0 || path[off] != _T('.'))
return false;
if (off > 0 && path[off-1] != PATH_SEPARATOR)
return false;
if (path[off+1] != _T('.'))
return false;
return path[off+2] == _T('\0') || path[off+2] == PATH_SEPARATOR;
}
static String getHomeFolder();
static String getApplicationFolder();
static String getCurrentFolder();
static String getProcessFolder() {
return getFilePath(getAppName());
}
static String ensureUnitPath(const String& path)
{
if (path.find(_T(" ")) == String::npos)
return path;
return String(_T("\"")+path+_T("\""));
}
static String& simplifyPath(String& path) {
// compress path by removing all "./" and "folder/../" occurrences
// (if path only, it should end in path-separator)
{
// removes all "./" occurrences
String::size_type i(0);
while ((i = path.find(_T(".") PATH_SEPARATOR_STR, i)) != String::npos) {
if (i > 0 && path[i-1] != PATH_SEPARATOR)
i += 2;
else
path.erase(i, 2);
}}
{
// removes all "folder/../" occurrences
String::size_type i(0);
while ((i = path.find(_T("..") PATH_SEPARATOR_STR, i)) != String::npos) {
if (i > 1 && path[i-1] == PATH_SEPARATOR) {
String::size_type prev = path.rfind(PATH_SEPARATOR, i-2);
if (prev == String::npos) prev = 0; else ++prev;
if (!isParentFolder(path, (int)prev)) {
path.erase(prev, i+3-prev);
i = prev;
continue;
}
}
i += 3;
}}
return path;
}
static String getSimplifiedPath(String path) {
return simplifyPath(path);
}
static String getRelativePath(const String& currentPath, const String& targetPath) {
// returns the path to the target relative to the current path;
// both current and target paths must be full paths;
// current path is assumed to be a folder, while target path can be also a file
CLISTDEF2(String) currentPathValues, targetPathValues;
Util::strSplit(currentPath, PATH_SEPARATOR, currentPathValues);
if (currentPathValues.back().empty())
currentPathValues.pop_back();
Util::strSplit(targetPath, PATH_SEPARATOR, targetPathValues);
size_t idxCurrentPath(0), idxTargetPath(0);
while (
idxCurrentPath < currentPathValues.size() &&
idxTargetPath < targetPathValues.size() &&
#ifdef _MSC_VER
_tcsicmp(currentPathValues[idxCurrentPath], targetPathValues[idxTargetPath]) == 0
#else
_tcscmp(currentPathValues[idxCurrentPath], targetPathValues[idxTargetPath]) == 0
#endif
)
++idxCurrentPath, ++idxTargetPath;
if (idxCurrentPath == 0)
return targetPath;
String relativePath;
relativePath.reserve(targetPath.size());
while (idxCurrentPath < currentPathValues.size()) {
relativePath += _T("..") PATH_SEPARATOR_STR;
++idxCurrentPath;
}
const size_t idxFirstTarget(idxTargetPath);
while (idxTargetPath < targetPathValues.size()) {
if (idxTargetPath > idxFirstTarget)
relativePath += PATH_SEPARATOR;
relativePath += targetPathValues[idxTargetPath++];
}
return relativePath;
}
static String& getCommonPath(String& commonPath, const String& path) {
// returns the path shared by the given paths
#ifdef _MSC_VER
while (_tcsnicmp(commonPath, path, commonPath.length()) != 0) {
#else
while (_tcsncmp(commonPath, path, commonPath.length()) != 0) {
#endif
commonPath.pop_back();
commonPath = getFilePath(commonPath);
}
return commonPath;
}
static String getCommonPath(const String* arrPaths, size_t numPaths) {
// returns the path shared by all given paths
ASSERT(numPaths > 0);
String commonPath(arrPaths[0]);
for (size_t i=1; !commonPath.empty() && i<numPaths; ++i)
getCommonPath(commonPath, arrPaths[i]);
return commonPath;
}
static String getFilePath(const String& path) {
const String::size_type size = path.size();
if (size < 3)
return String();
const String::size_type i = path.rfind(PATH_SEPARATOR, size-2);
return (i != String::npos) ? path.substr(0, i+1) : String();
}
static String getFileFullName(const String& path) {
const String::size_type i = path.rfind('.');
return (i != String::npos) ? String(path.substr(0, i)) : path;
}
static String getFileNameExt(const String& path) {
const String::size_type i = path.rfind(PATH_SEPARATOR);
if (i != String::npos)
return path.substr(i+1);
return path;
}
static String getFileName(const String& path) {
String::size_type i = path.rfind(PATH_SEPARATOR);
if (i == String::npos) i = 0; else ++i;
String::size_type j = path.rfind('.');
if (j == String::npos) j = path.length();
return path.substr(i, j-i);
}
static String getFileExt(const String& path) {
const String::size_type i = path.rfind('.');
return (i != String::npos) ? path.substr(i) : String();
}
static String getLastDir(const String& path) {
const String::size_type i = path.rfind(PATH_SEPARATOR);
if (i == String::npos) return String();
const String::size_type j = path.rfind(PATH_SEPARATOR, i-1);
if (j != String::npos)
return path.substr(j+1, j-i-1);
return path;
}
static String insertBeforeFileExt(const String& path, const String& extra) {
const String::size_type i = path.rfind('.');
return (i != String::npos) ? String(path).insert(i, extra) : path+extra;
}
static String insertAfterFilePath(const String& path, const String& extra) {
const String::size_type i = path.rfind(PATH_SEPARATOR);
return (i != String::npos) ? String(path).insert(i+1, extra) : extra+path;
}
static int compareFileName(const String& path1, const String& path2) {
#ifdef _MSC_VER
return _tcsicmp(path1, path2);
#else // _MSC_VER
return _tcscmp(path1, path2);
#endif // _MSC_VER
}
static String getValue(const String& str, const String& label) {
String::size_type pos = str.find(label);
if (pos == String::npos)
return String();
pos = str.find(_T("="), pos);
if (pos == String::npos)
return String();
String::size_type end = str.find_first_of(_T(LINE_SEPARATOR_STR), ++pos);
return str.substr(pos, end-pos);
}
// remove all instances of a given sequence of characters from the beginning and end of the given string
static String& strTrim(String& str, const String& strTrim) {
if (str.empty())
return str;
while (str.substr(0, strTrim.size()) == strTrim)
str.erase(0, strTrim.size());
while (str.substr(str.size()-strTrim.size(), strTrim.size()) == strTrim)
str.erase(str.size()-strTrim.size(), strTrim.size());
return str;
}
// split an input string with a delimiter and fill a string vector
static void strSplit(const String& str, TCHAR delim, CLISTDEF2(String)& values) {
values.Empty();
String::size_type start(0), end(0);
while (end != String::npos) {
end = str.find(delim, start);
values.AddConstruct(str.substr(start, end-start));
start = end + 1;
}
}
static void strSplit(const String& str, const String& delim, CLISTDEF2(String)& values) {
ASSERT(!delim.empty());
values.Empty();
String::size_type start(0), end(0);
while (end != String::npos) {
end = str.find(delim, start);
values.AddConstruct(str.substr(start, end-start));
start = end + delim.size();
}
}
static String getShortTimeString() {
char buf[8];
time_t _tt = time(NULL);
tm* _tm = localtime(&_tt);
if (_tm == NULL) {
strcpy(buf, "xx:xx");
} else {
strftime(buf, 8, "%H:%M", _tm);
}
return buf;
}
static String getTimeString() {
char buf[64];
time_t _tt;
time(&_tt);
tm* _tm = localtime(&_tt);
if (_tm == NULL) {
strcpy(buf, "xx:xx:xx");
} else {
strftime(buf, 64, "%X", _tm);
}
return buf;
}
static String formatBytes(int64_t aBytes) {
if (aBytes < (int64_t)1024) {
return String::FormatString("%dB", (uint32_t)aBytes&0xffffffff);
} else if (aBytes < (int64_t)1024*1024) {
return String::FormatString("%.02fKB", (double)aBytes/(1024.0));
} else if (aBytes < (int64_t)1024*1024*1024) {
return String::FormatString("%.02fMB", (double)aBytes/(1024.0*1024.0));
} else if (aBytes < (int64_t)1024*1024*1024*1024) {
return String::FormatString("%.02fGB", (double)aBytes/(1024.0*1024.0*1024.0));
} else {
return String::FormatString("%.02fTB", (double)aBytes/(1024.0*1024.0*1024.0*1024.0));
}
}
// format given time in milliseconds to higher units
static String formatTime(int64_t sTime, uint32_t nAproximate = 0) {
char buf[128];
uint32_t len = 0;
uint32_t nrNumbers = 0;
uint32_t rez = (uint32_t)(sTime / ((int64_t)24*3600*1000));
if (rez) {
++nrNumbers;
len += _sntprintf(buf+len, 128, "%ud", rez);
}
if (nAproximate > 3 && nrNumbers > 0)
return buf;
rez = (uint32_t)((sTime%((int64_t)24*3600*1000)) / (3600*1000));
if (rez) {
++nrNumbers;
len += _sntprintf(buf+len, 128, "%uh", rez);
}
if (nAproximate > 2 && nrNumbers > 0)
return buf;
rez = (uint32_t)((sTime%((int64_t)3600*1000)) / (60*1000));
if (rez) {
++nrNumbers;
len += _sntprintf(buf+len, 128, "%um", rez);
}
if (nAproximate > 1 && nrNumbers > 0)
return buf;
rez = (uint32_t)((sTime%((int64_t)60*1000)) / (1*1000));
if (rez) {
++nrNumbers;
len += _sntprintf(buf+len, 128, "%us", rez);
}
if (nAproximate > 0 && nrNumbers > 0)
return buf;
rez = (uint32_t)(sTime%((int64_t)1*1000));
if (rez || !nrNumbers)
len += _sntprintf(buf+len, 128, "%ums", rez);
return String(buf, len);
}
static String toString(const wchar_t* wsz) {
if (wsz == NULL)
return String();
#if 1
return std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t>().to_bytes(wsz);
#elif 1
std::mbstate_t state = std::mbstate_t();
const size_t len(std::wcsrtombs(NULL, &wsz, 0, &state));
if (len == static_cast<std::size_t>(-1))
return String();
std::vector<char> mbstr(len+1);
if (std::wcsrtombs(&mbstr[0], &wsz, mbstr.size(), &state) == static_cast<std::size_t>(-1))
return String();
return String(&mbstr[0]);
#else
const std::wstring ws(wsz);
const std::locale locale("");
typedef std::codecvt<wchar_t, char, std::mbstate_t> converter_type;
const converter_type& converter = std::use_facet<converter_type>(locale);
std::vector<char> to(ws.length() * converter.max_length());
std::mbstate_t state;
const wchar_t* from_next;
char* to_next;
if (converter.out(state, ws.data(), ws.data() + ws.length(), from_next, &to[0], &to[0] + to.size(), to_next) != converter_type::ok)
return String();
return std::string(&to[0], to_next);
#endif
}
static int64_t toInt64(LPCTSTR aString) {
#ifdef _MSC_VER
return _atoi64(aString);
#else
return atoll(aString);
#endif
}
static int toInt(LPCTSTR aString) {
return atoi(aString);
}
static uint32_t toUInt32Hex(LPCTSTR aString) {
uint32_t val;
sscanf(aString, "%x", &val);
return val;
}
static uint32_t toUInt32(LPCTSTR aString) {
return (uint32_t)atoi(aString);
}
static double toDouble(LPCTSTR aString) {
return atof(aString);
}
static float toFloat(LPCTSTR aString) {
return (float)atof(aString);
}
static time_t getTime() {
return (time_t)time(NULL);
}
static uint32_t getTick() {
#ifdef _MSC_VER
return GetTickCount();
#else
timeval tv;
gettimeofday(&tv, NULL);
return (uint32_t)(tv.tv_sec * 1000 ) + (tv.tv_usec / 1000);
#endif
}
/**
* IPRT - CRC64.
*
* The method to compute the CRC64 is referred to as CRC-64-ISO:
* http://en.wikipedia.org/wiki/Cyclic_redundancy_check
* The generator polynomial is x^64 + x^4 + x^3 + x + 1.
* Reverse polynom: 0xd800000000000000ULL
*
* As in: http://www.virtualbox.org/svn/vbox/trunk/src/VBox/Runtime/common/checksum/crc64.cpp
*/
/**
* Calculate CRC64 for a memory block.
*
* @returns CRC64 for the memory block.
* @param pv Pointer to the memory block.
* @param cb Size of the memory block in bytes.
*/
static uint64_t CRC64(const void *pv, size_t cb);
/**
* Start a multiblock CRC64 calculation.
*
* @returns Start CRC64.
*/
static uint64_t CRC64Start() {
return 0ULL;
}
/**
* Processes a multiblock of a CRC64 calculation.
*
* @returns Intermediate CRC64 value.
* @param uCRC64 Current CRC64 intermediate value.
* @param pv The data block to process.
* @param cb The size of the data block in bytes.
*/
static uint64_t CRC64Process(uint64_t uCRC64, const void *pv, size_t cb);
/**
* Complete a multiblock CRC64 calculation.
*
* @returns CRC64 value.
* @param uCRC64 Current CRC64 intermediate value.
*/
static uint64_t CRC64Finish(uint64_t uCRC64) {
return uCRC64;
}
static void Init();
static String GetCPUInfo();
static String GetRAMInfo();
static String GetOSInfo();
static String GetDiskInfo(const String&);
enum CPUFNC {NA=0, SSE, AVX};
static const Flags ms_CPUFNC;
static void LogBuild();
static void LogMemoryInfo();
static LPSTR* CommandLineToArgvA(LPCSTR CmdLine, size_t& _argc);
static String CommandLineToString(size_t argc, LPCTSTR* argv) {
String strCmdLine;
for (size_t i=1; i<argc; ++i)
strCmdLine += _T(" ") + String(argv[i]);
return strCmdLine;
}
struct Progress {
const String msg; // custom message header (ex: "Processed images")
const size_t total; // total number of jobs to be processed
const Timer::Type slp; // how often to display the progress (in ms)
const Timer::SysType start; // time when the work started
Timer::Type lastElapsed; // time when the last progress was displayed
size_t lastMsgLen; // how many characters had the last message
volatile size_t processed; // number of jobs already processed
CriticalSection cs; // multi-threading safety only for the incremental operator
Progress(const String& _msg, size_t _total, Timer::Type _slp=100/*ms*/)
: msg(_msg), total(_total), slp(_slp), start(Timer::GetSysTime()), lastElapsed(0), lastMsgLen(0), processed(0) {}
~Progress() { if (processed) close(); }
void operator++ () {
Thread::safeInc((Thread::safe_t&)processed);
if (cs.TryEnter()) {
process();
cs.Leave();
}
}
void display(size_t done) {
processed = done;
process();
}
void displayRemaining(size_t remaining) {
processed = total-remaining;
process();
}
void process() {
// make sure we don't print the progress too often
const Timer::Type elapsed(Timer::SysTime2TimeMs(Timer::GetSysTime()-start));
if (elapsed-lastElapsed < slp)
return;
lastElapsed = elapsed;
// compute percentage, elapsed and ETA
const size_t done(processed);
const float percentage((float)done/(float)total);
const Timer::Type remaining(percentage<0.01f && (done<10 || elapsed<10*1000) ? Timer::Type(0) : elapsed/percentage - elapsed);
// display progress
print(String::FormatString(_T("%s %u (%.2f%%, %s, ETA %s)..."), msg.c_str(), done, percentage*100.f, formatTime((int64_t)elapsed,1).c_str(), formatTime((int64_t)remaining,2).c_str()));
}
void close() {
// make sure we print the complete progress
const Timer::Type elapsed(Timer::SysTime2TimeMs(Timer::GetSysTime()-start));
// display progress
print(String::FormatString(_T("%s %u (100%%, %s)"), msg.c_str(), total, formatTime((int64_t)elapsed).c_str()));
std::cout << _T("\n");
processed = 0;
}
void print(const String& line) {
// print given line and make sure the last line is erased
const size_t msgLen = line.length();
std::cout << _T("\r") << line;
if (lastMsgLen > msgLen)
std::cout << String(lastMsgLen-msgLen, _T(' '));
std::cout << std::flush;
lastMsgLen = msgLen;
}
};
};
/*----------------------------------------------------------------*/
} // namespace SEACAVE
#endif // __SEACAVE_UTIL_H__