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
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__
|
|
|