1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
|
// **************************************************************************
// * This file is part of the FreeFileSync project. It is distributed under *
// * GNU General Public License: http://www.gnu.org/licenses/gpl-3.0 *
// * Copyright (C) Zenju (zenju AT gmx DOT de) - All Rights Reserved *
// **************************************************************************
#include "zstring.h"
#include <stdexcept>
#ifdef ZEN_WIN
#include "win.h"
#endif
using namespace zen;
/*
Perf test: compare strings 10 mio times; 64 bit build
-----------------------------------------------------
string a = "Fjk84$%kgfj$%T\\\\Gffg\\gsdgf\\fgsx----------d-"
string b = "fjK84$%kgfj$%T\\\\gfFg\\gsdgf\\fgSy----------dfdf"
Windows (UTF16 wchar_t)
4 ns | wcscmp
67 ns | CompareStringOrdinalFunc+ + bIgnoreCase
314 ns | LCMapString + wmemcmp
OS X (UTF8 char)
6 ns | strcmp
98 ns | strcasecmp
120 ns | strncasecmp + std::min(sizeLhs, sizeRhs);
856 ns | CFStringCreateWithCString + CFStringCompare(kCFCompareCaseInsensitive)
1110 ns | CFStringCreateWithCStringNoCopy + CFStringCompare(kCFCompareCaseInsensitive)
________________________
time per call | function
*/
#ifdef ZEN_WIN
int cmpStringNoCase(const wchar_t* lhs, size_t lhsLen, const wchar_t* rhs, size_t rhsLen)
{
assert(std::find(lhs, lhs + lhsLen, 0) == lhs + lhsLen); //don't expect embedded nulls!
assert(std::find(rhs, rhs + rhsLen, 0) == rhs + rhsLen); //
#ifdef ZEN_WIN_VISTA_AND_LATER
//"CompareStringOrdinal" (available since Windows Vista) is by a factor ~3 faster than old string comparison using "LCMapString"
const int rv = ::CompareStringOrdinal(lhs, //__in LPCWSTR lpString1,
static_cast<int>(lhsLen), //__in int cchCount1,
rhs, //__in LPCWSTR lpString2,
static_cast<int>(rhsLen), //__in int cchCount2,
true); //__in BOOL bIgnoreCase
if (rv <= 0)
throw std::runtime_error("Error comparing strings (CompareStringOrdinal). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
else
return rv - 2; //convert to C-style string compare result
#else
//do NOT use "CompareString"; this function is NOT meant for file name comparisons (even with LOCALE_INVARIANT and SORT_STRINGSORT): for example "weiß" == "weiss"!!!
//the only reliable way to compare filepaths (with XP) is to call "CharUpper" or "LCMapString":
const auto minSize = std::min(lhsLen, rhsLen);
if (minSize == 0) //LCMapString does not allow input sizes of 0!
return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
auto copyToUpperCase = [minSize](const wchar_t* strIn, wchar_t* strOut)
{
//faster than CharUpperBuff + wmemcpy or CharUpper + wmemcpy and same speed like ::CompareString()
if (::LCMapString(LOCALE_INVARIANT, //__in LCID Locale,
LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
strIn, //__in LPCTSTR lpSrcStr,
static_cast<int>(minSize), //__in int cchSrc,
strOut, //__out LPTSTR lpDestStr,
static_cast<int>(minSize)) == 0) //__in int cchDest
throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
};
auto eval = [&](wchar_t* bufL, wchar_t* bufR)
{
copyToUpperCase(lhs, bufL);
copyToUpperCase(rhs, bufR);
const int rv = ::wcsncmp(bufL, bufR, minSize);
if (rv != 0)
return rv;
return static_cast<int>(lhsLen) - static_cast<int>(rhsLen);
};
if (minSize <= MAX_PATH) //performance optimization: stack
{
wchar_t bufferL[MAX_PATH] = {};
wchar_t bufferR[MAX_PATH] = {};
return eval(bufferL, bufferR);
}
else //use freestore
{
std::vector<wchar_t> buffer(2 * minSize);
return eval(&buffer[0], &buffer[minSize]);
}
#endif
}
void makeUpperInPlace(wchar_t* str, size_t strLen)
{
//- use Windows' upper case conversion: faster than ::CharUpper()
//- LOCALE_INVARIANT is NOT available with Windows 2000 -> ok
//- MSDN: "The destination string can be the same as the source string only if LCMAP_UPPERCASE or LCMAP_LOWERCASE is set"
if (strLen != 0) //LCMapString does not allow input sizes of 0!
if (::LCMapString(LOCALE_INVARIANT, //__in LCID Locale,
LCMAP_UPPERCASE, //__in DWORD dwMapFlags,
str, //__in LPCTSTR lpSrcStr,
static_cast<int>(strLen), //__in int cchSrc,
str, //__out LPTSTR lpDestStr,
static_cast<int>(strLen)) == 0) //__in int cchDest
throw std::runtime_error("Error comparing strings (LCMapString). " + std::string(__FILE__) + ":" + numberTo<std::string>(__LINE__));
}
#endif
|