summaryrefslogtreecommitdiff
path: root/shared/zstring.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'shared/zstring.cpp')
-rw-r--r--shared/zstring.cpp123
1 files changed, 95 insertions, 28 deletions
diff --git a/shared/zstring.cpp b/shared/zstring.cpp
index cb288ea2..c3d5ba8e 100644
--- a/shared/zstring.cpp
+++ b/shared/zstring.cpp
@@ -4,6 +4,7 @@
#ifdef FFS_WIN
#include <wx/msw/wrapwin.h> //includes "windows.h"
#include "dllLoader.h"
+#include <boost/scoped_array.hpp>
#endif //FFS_WIN
#ifdef __WXDEBUG__
@@ -45,60 +46,122 @@ AllocationCount& AllocationCount::getInstance()
}
#endif
-
#ifdef FFS_WIN
+bool hasInvariantLocale()
+{
+ OSVERSIONINFO osvi;
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+
+ //invariant locale has been introduced with XP
+ if (GetVersionEx(&osvi))
+ return osvi.dwMajorVersion > 5 ||
+ (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion >= 1); //XP has majorVersion == 5, minorVersion == 1
+ //overview: http://msdn.microsoft.com/en-us/library/ms724834(VS.85).aspx
+ return false;
+}
#ifndef LOCALE_INVARIANT
#define LOCALE_INVARIANT 0x007f
#endif
+//warning: LOCALE_INVARIANT is NOT available with Windows 2000, so we have to make yet another distinction...
+namespace
+{
+const LCID invariantLocale = hasInvariantLocale() ?
+ LOCALE_INVARIANT :
+ MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); //see: http://msdn.microsoft.com/en-us/goglobal/bb688122.aspx
+}
+
inline
-int compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount = -1, const int bCount = -1)
+int compareFilenamesWin32(const wchar_t* a, const wchar_t* b, size_t sizeA, size_t sizeB)
{
- //try to call "CompareStringOrdinal" first for low-level string comparison: unfortunately available not before Windows Vista!
+ //try to call "CompareStringOrdinal" for low-level string comparison: unfortunately available not before Windows Vista!
+ //by a factor ~3 faster than old string comparison using "LCMapString"
typedef int (WINAPI *CompareStringOrdinalFunc)(
LPCWSTR lpString1,
int cchCount1,
LPCWSTR lpString2,
int cchCount2,
BOOL bIgnoreCase);
- static const CompareStringOrdinalFunc ordinalCompare = Utility::loadDllFunKernel<CompareStringOrdinalFunc>("CompareStringOrdinal");
+ static const CompareStringOrdinalFunc ordinalCompare = Utility::loadDllFunction<CompareStringOrdinalFunc>(L"kernel32.dll", "CompareStringOrdinal");
-
- //we're lucky here! This additional test for "CompareStringOrdinal" has no noticeable performance impact!!
- if (ordinalCompare != NULL)
+ if (ordinalCompare != NULL) //this additional test has no noticeable performance impact
{
const int rv = (*ordinalCompare)(
a, //pointer to first string
- aCount, //size, in bytes or characters, of first string
+ sizeA, //size, in bytes or characters, of first string
b, //pointer to second string
- bCount, //size, in bytes or characters, of second string
+ sizeB, //size, in bytes or characters, of second string
true); //ignore case
-
if (rv == 0)
throw std::runtime_error("Error comparing strings (ordinal)!");
else
return rv - 2; //convert to C-style string compare result
}
- else //fallback to "CompareString". Attention: this function is NOT accurate: for example "weiß" == "weiss"!!!
+ else //fallback
{
- //DON'T use lstrcmpi() here! It uses word sort and is locale dependent!
- //Use CompareString() with "SORT_STRINGSORT" instead!!!
+//do NOT use "CompareString"; this function is NOT accurate (even with LOCALE_INVARIANT and SORT_STRINGSORT): for example "weiß" == "weiss"!!!
+//the only reliable way to compare filenames (with XP) is to call "CharUpper" or "LCMapString":
- const int rv = CompareString(
- LOCALE_INVARIANT, //locale independent
- NORM_IGNORECASE | SORT_STRINGSORT, //comparison-style options
- a, //pointer to first string
- aCount, //size, in bytes or characters, of first string
- b, //pointer to second string
- bCount); //size, in bytes or characters, of second string
+ const size_t minSize = std::min(sizeA, sizeB);
- if (rv == 0)
- throw std::runtime_error("Error comparing strings!");
- else
- return rv - 2; //convert to C-style string compare result
+ if (minSize == 0) //LCMapString does not allow input sizes of 0!
+ return sizeA - sizeB;
+
+ int rv = 0; //always initialize...
+ if (minSize <= 5000) //performance optimization: stack
+ {
+ wchar_t bufferA[5000];
+ wchar_t bufferB[5000];
+
+ if (::LCMapString( //faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString()
+ invariantLocale, //__in LCID Locale,
+ LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
+ a, //__in LPCTSTR lpSrcStr,
+ minSize, //__in int cchSrc,
+ bufferA, //__out LPTSTR lpDestStr,
+ 5000 //__in int cchDest
+ ) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString)");
+
+ if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, b, minSize, bufferB, 5000) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString)");
+
+ rv = ::wmemcmp(bufferA, bufferB, minSize);
+ }
+ else //use freestore
+ {
+ boost::scoped_array<wchar_t> bufferA(new wchar_t[minSize]);
+ boost::scoped_array<wchar_t> bufferB(new wchar_t[minSize]);
+
+ if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, a, minSize, bufferA.get(), minSize) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString: FS)");
+
+ if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, b, minSize, bufferB.get(), minSize) == 0)
+ throw std::runtime_error("Error comparing strings! (LCMapString: FS)");
+
+ rv = ::wmemcmp(bufferA.get(), bufferB.get(), minSize);
+ }
+
+ return rv == 0 ?
+ sizeA - sizeB :
+ rv;
}
+
+// const int rv = CompareString(
+// invariantLocale, //locale independent
+// NORM_IGNORECASE | SORT_STRINGSORT, //comparison-style options
+// a, //pointer to first string
+// aCount, //size, in bytes or characters, of first string
+// b, //pointer to second string
+// bCount); //size, in bytes or characters, of second string
+//
+// if (rv == 0)
+// throw std::runtime_error("Error comparing strings!");
+// else
+// return rv - 2; //convert to C-style string compare result
}
#endif
@@ -106,13 +169,13 @@ int compareStringsWin32(const wchar_t* a, const wchar_t* b, const int aCount = -
#ifdef FFS_WIN
int Zstring::CmpNoCase(const DefaultChar* other) const
{
- return ::compareStringsWin32(c_str(), other); //way faster than wxString::CmpNoCase()!!
+ return ::compareFilenamesWin32(c_str(), other, length(), ::wcslen(other)); //way faster than wxString::CmpNoCase()
}
int Zstring::CmpNoCase(const Zstring& other) const
{
- return ::compareStringsWin32(c_str(), other.c_str(), length(), other.length()); //way faster than wxString::CmpNoCase()!!
+ return ::compareFilenamesWin32(c_str(), other.c_str(), length(), other.length()); //way faster than wxString::CmpNoCase()
}
#endif
@@ -277,14 +340,17 @@ std::vector<Zstring> Zstring::Tokenize(const DefaultChar delimiter) const
#ifdef FFS_WIN
-Zstring& Zstring::MakeLower()
+Zstring& Zstring::MakeUpper()
{
const size_t thisLen = length();
if (thisLen == 0)
return *this;
reserve(thisLen); //make unshared
- ::CharLower(data()); //use Windows' lower case conversion
+
+ //use Windows' upper case conversion: faster than ::CharUpper()
+ if (::LCMapString(invariantLocale, LCMAP_UPPERCASE, data(), thisLen, data(), thisLen) == 0)
+ throw std::runtime_error("Error converting to upper case! (LCMapString)");
return *this;
}
@@ -484,3 +550,4 @@ void Zstring::reserve(size_t capacityNeeded) //make unshared and check capacity
descr->capacity = newCapacity;
}
}
+
bgstack15