///////////////////////////////////////////////////////////////////////////////
// MuldeR's Utilities for Qt
-// Copyright (C) 2004-2016 LoRd_MuldeR <MuldeR2@GMX.de>
+// Copyright (C) 2004-2018 LoRd_MuldeR <MuldeR2@GMX.de>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
//MUtils
#include <MUtils/Global.h>
#include <MUtils/OSSupport.h>
+#include <MUtils/Version.h>
//Internal
#include "DirLocker.h"
#include <QPair>
#include <QListIterator>
#include <QMutex>
+#include <QThreadStorage>
//CRT
#include <cstdlib>
#define rand_s(X) (true)
#endif
+//Per-thread init flag
+static QThreadStorage<bool> g_srand_flag;
+
+//32-Bit wrapper for qrand()
+#define QRAND() ((static_cast<quint32>(qrand()) & 0xFFFF) | (static_cast<quint32>(qrand()) << 16U))
+
//Robert Jenkins' 96 bit Mix Function
-static unsigned int mix_function(const unsigned int x, const unsigned int y, const unsigned int z)
+static quint32 mix_function(quint32 a, quint32 b, quint32 c)
{
- unsigned int a = x;
- unsigned int b = y;
- unsigned int c = z;
-
a=a-b; a=a-c; a=a^(c >> 13);
- b=b-c; b=b-a; b=b^(a << 8 );
+ b=b-c; b=b-a; b=b^(a << 8);
c=c-a; c=c-b; c=c^(b >> 13);
a=a-b; a=a-c; a=a^(c >> 12);
b=b-c; b=b-a; b=b^(a << 16);
- c=c-a; c=c-b; c=c^(b >> 5 );
- a=a-b; a=a-c; a=a^(c >> 3 );
+ c=c-a; c=c-b; c=c^(b >> 5);
+ a=a-b; a=a-c; a=a^(c >> 3);
b=b-c; b=b-a; b=b^(a << 10);
c=c-a; c=c-b; c=c^(b >> 15);
return a ^ b ^ c;
}
-void MUtils::seed_rand(void)
+static void seed_rand(void)
{
- qsrand(mix_function(clock(), time(NULL), _getpid()));
+ QDateTime build(MUtils::Version::lib_build_date(), MUtils::Version::lib_build_time());
+ const quint32 seed_0 = mix_function(MUtils::OS::process_id(), MUtils::OS::thread_id(), build.toMSecsSinceEpoch());
+ qsrand(mix_function(clock(), time(NULL), seed_0));
+}
+
+static quint32 rand_fallback(void)
+{
+ Q_ASSERT(RAND_MAX >= 0x7FFF);
+
+ if (!(g_srand_flag.hasLocalData() && g_srand_flag.localData()))
+ {
+ seed_rand();
+ g_srand_flag.setLocalData(true);
+ }
+
+ quint32 rnd_val = mix_function(0x32288EA3, clock(), time(NULL));
+
+ for (size_t i = 0; i < 42; i++)
+ {
+ rnd_val = mix_function(rnd_val, QRAND(), QRAND());
+ rnd_val = mix_function(QRAND(), rnd_val, QRAND());
+ rnd_val = mix_function(QRAND(), QRAND(), rnd_val);
+ }
+
+ return rnd_val;
}
quint32 MUtils::next_rand_u32(void)
quint32 rnd;
if (rand_s(&rnd))
{
- for (size_t i = 0; i < sizeof(quint32); i += 2)
- {
- rnd = (rnd << 16) ^ qrand();
- }
+ return rand_fallback();
}
return rnd;
}
{
return true;
}
+ MUtils::OS::sleep_ms(1);
fileInfo.refresh();
}
{
return true;
}
+ MUtils::OS::sleep_ms(1);
folder.refresh();
}
// CLEAN FILE PATH
///////////////////////////////////////////////////////////////////////////////
-QString MUtils::clean_file_name(const QString &name)
+static QMutex g_clean_file_name_mutex;
+static QScopedPointer<const QList<QPair<QRegExp,QString>>> g_clean_file_name_regex;
+
+static void clean_file_name_make_pretty(QString &str)
+{
+ static const struct { const char *p; const char *r; } PATTERN[] =
+ {
+ { "^\\s*\"([^\"]*)\"\\s*$", "\\1" }, //Remove straight double quotes around the whole string
+ { "\"([^\"]*)\"", "\xE2\x80\x9C\\1\xE2\x80\x9D" }, //Replace remaining pairs of straight double quotes with opening/closing double quote
+ { "^[\\\\/:]+([^\\\\/:]+.*)$", "\\1" }, //Remove leading slash, backslash and colon characters
+ { "^(.*[^\\\\/:]+)[\\\\/:]+$", "\\1" }, //Remove trailing slash, backslash and colon characters
+ { "(\\s*[\\\\/:]\\s*)+", " - " }, //Replace any slash, backslash or colon character that appears in the middle
+ { NULL, NULL }
+ };
+
+ QMutexLocker locker(&g_clean_file_name_mutex);
+
+ if (g_clean_file_name_regex.isNull())
+ {
+ QScopedPointer<QList<QPair<QRegExp, QString>>> list(new QList<QPair<QRegExp, QString>>());
+ for (size_t i = 0; PATTERN[i].p; ++i)
+ {
+ list->append(qMakePair(QRegExp(QString::fromUtf8(PATTERN[i].p), Qt::CaseInsensitive), PATTERN[i].r ? QString::fromUtf8(PATTERN[i].r) : QString()));
+ }
+ g_clean_file_name_regex.reset(list.take());
+ }
+
+ bool keepOnGoing = !str.isEmpty();
+ while(keepOnGoing)
+ {
+ const QString prev = str;
+ keepOnGoing = false;
+ for (QList<QPair<QRegExp, QString>>::ConstIterator iter = g_clean_file_name_regex->constBegin(); iter != g_clean_file_name_regex->constEnd(); ++iter)
+ {
+ str.replace(iter->first, iter->second);
+ if (str.compare(prev))
+ {
+ str = str.simplified();
+ keepOnGoing = !str.isEmpty();
+ break;
+ }
+ }
+ }
+}
+
+QString MUtils::clean_file_name(const QString &name, const bool &pretty)
{
static const QLatin1Char REPLACEMENT_CHAR('_');
static const char FILENAME_ILLEGAL_CHARS[] = "<>:\"/\\|?*";
};
QString result(name);
- if (result.contains(QLatin1Char('"')))
+ if (pretty)
{
- QRegExp quoted("\"(.+)\"");
- quoted.setMinimal(true);
- result.replace(quoted, "``\\1ยดยด");
+ clean_file_name_make_pretty(result);
}
for(QString::Iterator iter = result.begin(); iter != result.end(); iter++)
return qMakePair(QString(), posixPath);
}
-QString MUtils::clean_file_path(const QString &path)
+QString MUtils::clean_file_path(const QString &path, const bool &pretty)
{
const QPair<QString, QString> prefix = clean_file_path_get_prefix(path);
{
continue; //handle case "c:\"
}
- parts[i] = MUtils::clean_file_name(parts[i]);
+ parts[i] = MUtils::clean_file_name(parts[i], pretty);
}
const QString cleanPath = parts.join(QLatin1String("/"));
bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 &value)
{
- return regexp_parse_uint32(regexp, &value, 1);
+ return regexp_parse_uint32(regexp, &value, 1U, 1U);
+}
+
+bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 &value)
+{
+ return regexp_parse_int32(regexp, &value, 1U, 1U);
+}
+
+bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 &value, const size_t &offset)
+{
+ return regexp_parse_uint32(regexp, &value, offset, 1U);
+}
+
+bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 &value, const size_t &offset)
+{
+ return regexp_parse_int32(regexp, &value, offset, 1U);
}
bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 *values, const size_t &count)
{
+ return regexp_parse_uint32(regexp, values, 1U, count);
+}
+
+bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 *values, const size_t &count)
+{
+ return regexp_parse_int32(regexp, values, 1U, count);
+}
+
+bool MUtils::regexp_parse_uint32(const QRegExp ®exp, quint32 *values, const size_t &offset, const size_t &count)
+{
const QStringList caps = regexp.capturedTexts();
-
- if(caps.isEmpty() || (quint32(caps.count()) <= count))
+
+ if (caps.isEmpty() || (quint32(caps.count()) <= count))
+ {
+ return false;
+ }
+
+ for (size_t i = 0; i < count; i++)
+ {
+ bool ok = false;
+ values[i] = caps[offset+i].toUInt(&ok);
+ if (!ok)
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool MUtils::regexp_parse_int32(const QRegExp ®exp, qint32 *values, const size_t &offset, const size_t &count)
+{
+ const QStringList caps = regexp.capturedTexts();
+
+ if (caps.isEmpty() || (quint32(caps.count()) <= count))
{
return false;
}
- for(size_t i = 0; i < count; i++)
+ for (size_t i = 0; i < count; i++)
{
bool ok = false;
- values[i] = caps[i+1].toUInt(&ok);
- if(!ok)
+ values[i] = caps[offset+i].toInt(&ok);
+ if (!ok)
{
return false;
}
}
///////////////////////////////////////////////////////////////////////////////
+// FP MATH SUPPORT
+///////////////////////////////////////////////////////////////////////////////
+
+MUtils::fp_parts_t MUtils::break_fp(const double value)
+{
+ fp_parts_t result;
+ result.fractpart = modf(value, &result.intpart);
+ return result;
+}
+
+///////////////////////////////////////////////////////////////////////////////
// SELF-TEST
///////////////////////////////////////////////////////////////////////////////