-
Notifications
You must be signed in to change notification settings - Fork 901
/
util.h
792 lines (693 loc) · 36.2 KB
/
util.h
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
/*
AutoHotkey
Copyright 2003-2009 Chris Mallett (support@autohotkey.com)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
#ifndef util_h
#define util_h
#include "stdafx.h" // pre-compiled headers
#include "defines.h"
#ifdef _WIN64
#define Exp32or64(exp32, exp64) (exp64)
#else
#define Exp32or64(exp32, exp64) (exp32)
#endif
#ifdef UNICODE
#define tmemcpy wmemcpy
#define tmemmove wmemmove
#define tmemset wmemset
#define tmemcmp wmemcmp
#define tmalloc(c) ((LPTSTR) malloc((c) << 1))
#define trealloc(p, c) ((LPTSTR) realloc((p), (c) << 1))
#define talloca(c) ((LPTSTR) _alloca((c) << 1))
#else
#define tmemcpy (char*)memcpy
#define tmemmove memmove
#define tmemset memset
#define tmemcmp memcmp
#define tmalloc(c) ((LPTSTR) malloc(c))
#define trealloc(p, c) ((LPTSTR) realloc((p), (c)))
#define talloca(c) ((LPTSTR) _alloca(c))
#endif
#define IS_SPACE_OR_TAB(c) (c == ' ' || c == '\t')
#ifndef UNICODE
#define IS_SPACE_OR_TAB_OR_NBSP(c) (c == ' ' || c == '\t' || c == -96) // Use a negative to support signed chars.
#else
#define IS_SPACE_OR_TAB_OR_NBSP(c) IS_SPACE_OR_TAB(c) // wchar_t is unsigned
#endif
// v1.0.43.04: The following are macros to avoid crash bugs caused by improper casting, namely a failure to cast
// a signed char to UCHAR before promoting it to LPSTR, which crashes since CharLower/Upper would interpret
// such a high unsigned value as an address rather than a single char.
#define ltolower(ch) (TBYTE)(UINT_PTR)CharLower((LPTSTR)(TBYTE)(ch)) // "L" prefix stands for "locale", like lstrcpy.
#define ltoupper(ch) (TBYTE)(UINT_PTR)CharUpper((LPTSTR)(TBYTE)(ch)) // For performance, some callers don't want return value cast to char.
// Locale independent ctype (applied to the ASCII characters only).
inline int cisctype(TBYTE c, int type)
{
return (c & (~0x7F)) ? 0 : _isctype(c, type);
}
// These simplified checks significantly reduce code size vs. using the standard C functions:
inline int cisupper(TBYTE c) { return c <= 'Z' && c >= 'A'; }
inline int cislower(TBYTE c) { return c >= 'a' && c <= 'z'; }
inline int cisdigit(TBYTE c) { return c <= '9' && c >= '0'; }
inline int cisalpha(TBYTE c) { return cisupper(c) || cislower(c); }
inline int cisalnum(TBYTE c) { return ((c & ~0x7F) ? 0 : isalnum(c)); }
inline int cisxdigit(TBYTE c) { return ((c & ~0x7F) ? 0 : isxdigit(c)); }
inline int cisprint(TBYTE c) { return ((c & ~0x7F) ? 0 : isprint(c)); }
inline int cisspace(TBYTE c) { return ((c & ~0x7F) ? 0 : isspace(c)); }
// The results of toupper/tolower are implementations dependent (see below), though the test results are OK in VS2008's CRT.
// MDSN: In order for toupper to give the expected results, __isascii and islower must both return nonzero.
// Linux (man page): The value returned is that of the converted letter, or c if the conversion was not possible. (CONFORMING TO C89, C99, 4.3BSD.)
inline TCHAR ctoupper(TBYTE c)
{
return cislower(c) ? (c & ~0x20) : c;
}
inline TCHAR ctolower(TBYTE c)
{
return cisupper(c) ? (c | 0x20) : c;
}
// NOTE: MOVING THINGS OUT OF THIS FILE AND INTO util.cpp can hurt benchmarks by 10% or more, so be careful
// when doing so (even when the change seems inconsequential, it can impact benchmarks due to quirks of code
// generation and caching).
inline LPTSTR StrToTitleCase(LPTSTR aStr)
// Inline functions like the following probably don't actually get put inline by the compiler (since it knows
// it's not worth it). However, changing this to be non-inline has a significant impact on benchmarks even
// though the function is never called by those benchmarks, probably due to coincidences in how the generated
// code gets cached in the CPU. So it seems best not to mess with anything without making sure it doesn't
// drop the benchmarks significantly.
{
if (!aStr) return aStr;
LPTSTR aStr_orig = aStr;
for (bool convert_next_alpha_char_to_upper = true; *aStr; ++aStr)
{
if (IsCharAlpha(*aStr)) // Use this to better support chars from non-English languages.
{
if (convert_next_alpha_char_to_upper)
{
*aStr = ltoupper(*aStr);
convert_next_alpha_char_to_upper = false;
}
else
*aStr = ltolower(*aStr);
}
else
if (_istspace(*aStr))
convert_next_alpha_char_to_upper = true;
// Otherwise, it's a digit, punctuation mark, etc. so nothing needs to be done.
}
return aStr_orig;
}
template<typename T = LPTSTR>
inline T StrChrAny(T aStr, LPCTSTR aCharList)
// Returns the position of the first char in aStr that is of any one of the characters listed in aCharList.
// Returns NULL if not found.
// Update: Yes, this seems identical to strpbrk(). However, since the corresponding code would
// have to be added to the EXE regardless of which was used, there doesn't seem to be much
// advantage to switching (especially since if the two differ in behavior at all, things might
// get broken). Another reason is the name "strpbrk()" is not as easy to remember.
{
if (aStr == NULL || aCharList == NULL) return NULL;
if (!*aStr || !*aCharList) return NULL;
// Don't use strchr() because that would just find the first occurrence
// of the first search-char, which is not necessarily the first occurrence
// of *any* search-char:
LPCTSTR look_for_this_char;
TCHAR char_being_analyzed;
for (; *aStr; ++aStr)
// If *aStr is any of the search char's, we're done:
for (char_being_analyzed = *aStr, look_for_this_char = aCharList; *look_for_this_char; ++look_for_this_char)
if (char_being_analyzed == *look_for_this_char)
return aStr; // Match found.
return NULL; // No match.
}
inline LPCTSTR omit_leading_whitespace(LPCTSTR aBuf) // 10/17/2006: __forceinline didn't help significantly.
// While aBuf points to a whitespace, moves to the right and returns the first non-whitespace
// encountered.
{
for (; IS_SPACE_OR_TAB(*aBuf); ++aBuf);
return aBuf;
}
inline LPTSTR omit_leading_whitespace(LPTSTR aBuf)
{
return (LPTSTR) omit_leading_whitespace((LPCTSTR) aBuf);
}
template<typename T>
inline T omit_leading_any(T aBuf, LPCTSTR aOmitList, size_t aLength)
// Returns the address of the first character in aBuf that isn't a member of aOmitList.
// But no more than aLength characters of aBuf will be considered. If aBuf is composed
// entirely of omitted characters, the address of the char after the last char in the
// string will returned (that char will be the zero terminator unless aLength explicitly
// caused only part of aBuf to be considered).
{
LPCTSTR cp;
for (size_t i = 0; i < aLength; ++i, ++aBuf)
{
// Check if the current char is a member of the omitted-char list:
for (cp = aOmitList; *cp; ++cp)
if (*aBuf == *cp) // Match found.
break;
if (!*cp) // No match found, so this character is not omitted, thus we immediately return it's position.
return aBuf;
}
// Since the above didn't return, aBuf is the position of the zero terminator or (if aLength
// indicated only a substring) the position of the char after the last char in the substring.
return aBuf;
}
inline LPTSTR omit_trailing_whitespace(LPTSTR aBuf, LPTSTR aBuf_marker)
// aBuf_marker must be a position in aBuf (to the right of it).
// Starts at aBuf_marker and keeps moving to the left until a non-whitespace
// char is encountered. Returns the position of that char.
{
for (; aBuf_marker > aBuf && IS_SPACE_OR_TAB(*aBuf_marker); --aBuf_marker);
return aBuf_marker; // Can equal aBuf.
}
template<typename T>
inline size_t omit_trailing_any(T aBuf, LPCTSTR aOmitList, T aBuf_marker)
// aBuf_marker must be a position in aBuf (to the right of it).
// Starts at aBuf_marker and keeps moving to the left until a char that isn't a member
// of aOmitList is found. The length of the remaining substring is returned.
// That length will be zero if the string consists entirely of omitted characters.
{
LPCTSTR cp;
for (; aBuf_marker > aBuf; --aBuf_marker)
{
// Check if the current char is a member of the omitted-char list:
for (cp = aOmitList; *cp; ++cp)
if (*aBuf_marker == *cp) // Match found.
break;
if (!*cp) // No match found, so this character is not omitted, thus we immediately return.
return (aBuf_marker - aBuf) + 1; // The length of the string when trailing chars are omitted.
}
// Since the above didn't return, aBuf_marker is now equal to aBuf. If this final character is itself
// a member of the omitted-list, the length returned will be zero. Otherwise it will be 1:
for (cp = aOmitList; *cp; ++cp)
if (*aBuf_marker == *cp) // Match found.
return 0;
return 1;
}
inline size_t ltrim(LPTSTR aStr, size_t aLength = -1)
// Caller must ensure that aStr is not NULL.
// v1.0.25: Returns the length if it was discovered as a result of the operation, or aLength otherwise.
// NOTE: THIS VERSION trims only tabs and spaces. It specifically avoids
// trimming newlines because some callers want to retain those.
{
if (!*aStr) return 0;
LPTSTR ptr;
// Find the first non-whitespace char (which might be the terminator):
for (ptr = aStr; IS_SPACE_OR_TAB(*ptr); ++ptr); // Self-contained loop.
// v1.0.25: If no trimming needed, don't do the memmove. This seems to make a big difference
// in the performance of critical sections of the program:
size_t offset;
if (offset = ptr - aStr) // Assign.
{
if (aLength == -1)
aLength = _tcslen(ptr); // Set aLength as new/trimmed length, for use below and also as the return value.
else // v1.0.25.05 bug-fix: Must adjust the length provided by caller to reflect what we did here.
aLength -= offset;
tmemmove(aStr, ptr, aLength + 1); // +1 to include the '\0'. memmove() permits source & dest to overlap.
}
return aLength; // This will return -1 if the block above didn't execute and caller didn't specify the length.
}
inline size_t rtrim(LPTSTR aStr, size_t aLength = -1)
// Caller must ensure that aStr is not NULL.
// To improve performance, caller may specify a length (e.g. when it is already known).
// v1.0.25: Always returns the new length of the string.
// NOTE: THIS VERSION trims only tabs and spaces. It specifically avoids trimming newlines because
// some callers want to retain those.
{
if (!*aStr) return 0; // The below relies upon this check having been done.
// It's done this way in case aStr just happens to be address 0x00 (probably not possible
// on Intel & Intel-clone hardware) because otherwise --cp would decrement, causing an
// underflow since pointers are probably considered unsigned values, which would
// probably cause an infinite loop. Extremely unlikely, but might as well try
// to be thorough:
if (aLength == -1)
aLength = _tcslen(aStr); // Set aLength for use below and also as the return value.
for (LPTSTR cp = aStr + aLength - 1; ; --cp, --aLength)
{
if (!IS_SPACE_OR_TAB(*cp))
{
cp[1] = '\0';
return aLength;
}
// Otherwise, it is a space or tab...
if (cp == aStr) // ... and we're now at the first character of the string...
{
*cp = '\0'; // ... so the entire string is made empty.
return 0;
}
// else it's a space or tab, and there are still more characters to check. Let the loop
// do its decrements.
}
}
inline void rtrim_literal(LPTSTR aStr, TCHAR aLiteralMap[])
// Caller must ensure that aStr is not NULL.
// NOTE: THIS VERSION trims only tabs and spaces which aren't marked as literal (so not "`t" or "` ").
// It specifically avoids trimming newlines because some callers want to retain those.
{
if (!*aStr) return; // The below relies upon this check having been done.
// It's done this way in case aStr just happens to be address 0x00 (probably not possible
// on Intel & Intel-clone hardware) because otherwise --cp would decrement, causing an
// underflow since pointers are probably considered unsigned values, which would
// probably cause an infinite loop. Extremely unlikely, but might as well try
// to be thorough:
for (size_t last = _tcslen(aStr) - 1; ; --last)
{
if (!IS_SPACE_OR_TAB(aStr[last]) || aLiteralMap[last]) // It's not a space or tab, or it's a literal one.
{
aStr[last + 1] = '\0';
return;
}
// Otherwise, it is a space or tab...
if (last == 0) // ... and we're now at the first character of the string...
{
if (IS_SPACE_OR_TAB(aStr[last])) // ... and that first character is also a space or tab...
*aStr = '\0'; // ... so the entire string is made empty.
return; // ... and we return in any case.
}
// else it's a space or tab, and there are still more characters to check. Let the loop
// do its decrements.
}
}
inline size_t rtrim_with_nbsp(LPTSTR aStr, size_t aLength = -1)
// Returns the new length of the string.
// Caller must ensure that aStr is not NULL.
// To improve performance, caller may specify a length (e.g. when it is already known).
// Same as rtrim but also gets rid of those annoying nbsp (non breaking space) chars that sometimes
// wind up on the clipboard when copied from an HTML document, and thus get pasted into the text
// editor as part of the code (such as the sample code in some of the examples).
{
if (!*aStr) return 0; // The below relies upon this check having been done.
if (aLength == -1)
aLength = _tcslen(aStr); // Set aLength for use below and also as the return value.
for (LPTSTR cp = aStr + aLength - 1; ; --cp, --aLength)
{
if (!IS_SPACE_OR_TAB_OR_NBSP(*cp))
{
cp[1] = '\0';
return aLength;
}
if (cp == aStr)
{
if (IS_SPACE_OR_TAB_OR_NBSP(*cp)) // ... and that first character is also a space or tab...
{
*cp = '\0'; // ... so the entire string is made empty...
return 0; // Fix for v1.0.39: Must return 0 not aLength in this case.
}
return aLength; // ... and we return in any case.
}
}
}
inline size_t trim(LPTSTR aStr, size_t aLength = -1)
// Caller must ensure that aStr is not NULL.
// Returns new length of aStr.
// To improve performance, caller may specify a length (e.g. when it is already known).
// NOTE: THIS VERSION trims only tabs and spaces. It specifically avoids
// trimming newlines because some callers want to retain those.
{
aLength = ltrim(aStr, aLength); // It may return -1 to indicate that it still doesn't know the length.
return rtrim(aStr, aLength);
// v1.0.25: rtrim() always returns the new length of the string.
}
inline size_t strip_trailing_backslash(LPTSTR aPath)
// Removes any backslash (if there is one).
// Returns length of the new string to allow some callers to avoid another strlen() call.
{
size_t length = _tcslen(aPath);
if (!length) // Below relies on this check having been done to prevent underflow.
return length;
LPTSTR cp = aPath + length - 1;
if (*cp == _T('\\'))
{
*cp = '\0';
return length - 1;
}
// Otherwise there no slash to remove, so return the current length.
return length;
}
// Returns aBuf if not surrounded by matching quote marks (" or ').
// Otherwise returns aBuf+1 after terminating at the trailing quote mark.
inline LPTSTR strip_quote_marks(LPTSTR aBuf)
{
if (!aBuf || !(*aBuf == '"' || *aBuf == '\''))
return aBuf;
LPTSTR end = _tcschr(aBuf + 1, '\0');
if (end[-1] != *aBuf)
return aBuf;
end[-1] = '\0';
return aBuf + 1;
}
// For the following, checking for non-ASCII first avoids undefined behaviour in
// isalnum/isalpha, which produce smaller code than using cisalnum/cisalpha.
#define IS_IDENTIFIER_CHAR(c) (UINT(c) > 0x7F || isalnum(c) || (c) == '_')
#define IS_LEADING_IDENTIFIER_CHAR(c) (UINT(c) > 0x7F || isalpha(c) || (c) == '_')
template<typename T> inline T find_identifier_end(T aBuf)
// Locates the next character which is not valid in an identifier (var, func, or obj.key name).
{
while (IS_IDENTIFIER_CHAR(*aBuf)) ++aBuf;
return aBuf;
}
// Transformation is the same in either direction because the end bytes are swapped
// and the middle byte is left as-is:
#define bgr_to_rgb(aBGR) rgb_to_bgr(aBGR)
inline COLORREF rgb_to_bgr(DWORD aRGB)
// Fancier methods seem prone to problems due to byte alignment or compiler issues.
{
return RGB(GetBValue(aRGB), GetGValue(aRGB), GetRValue(aRGB));
}
inline bool IsHex(LPCTSTR aBuf) // 10/17/2006: __forceinline worsens performance, but physically ordering it near ATOI64() [via /ORDER] boosts by 3.5%.
// Note: AHK support for hex ints reduces performance by only 10% for decimal ints, even in the tightest of math loops.
{
// For whatever reason, omit_leading_whitespace() benches consistently faster (albeit slightly) than
// the same code put inline (confirmed again on 10/17/2006, though the difference is hardly anything):
//for (; IS_SPACE_OR_TAB(*aBuf); ++aBuf);
aBuf = omit_leading_whitespace(aBuf); // i.e. caller doesn't have to have ltrimmed.
if (!*aBuf)
return false;
if (*aBuf == '-' || *aBuf == '+')
++aBuf;
// The "0x" prefix must be followed by at least one hex digit, otherwise it's not considered hex:
#define IS_HEX(buf) (*buf == '0' && (*(buf + 1) == 'x' || *(buf + 1) == 'X') && isxdigit(*(buf + 2)))
return IS_HEX(aBuf);
}
__int64 istrtoi64(LPCTSTR buf, LPCTSTR *endptr);
inline __int64 istrtoi64(LPTSTR buf, LPTSTR *endptr)
{
return istrtoi64(buf, const_cast<LPCTSTR *>(endptr));
}
__int64 nstrtoi64(LPCTSTR buf);
// As of v1.0.30, ATOI(), ITOA() and the other related functions below are no longer macros
// because there are too many places where something like ATOI(++cp) is done, which would be a
// bug if not caught since cp would be incremented more than once if the macro referred to that
// arg more than once. In addition, a non-comprehensive, simple benchmark shows that the
// macros don't perform any better anyway, probably in part because there are many times when
// something like ArgToInt(1) is called, which forces the ARG1 macro to be expanded two or more
// times within ATOI (when it was a macro). So for now, the below are declared as inline.
// However, it seems that the compiler chooses not to make them truly inline, which as it
// turns out is probably the right decision since a simple benchmark shows that even with
// __forceinline in effect for all of them (which is confirmed to actually force inline),
// the performance isn't any better.
inline __int64 ATOI64(LPCTSTR buf)
{
// See ATOI() for the reason IsHex() is used.
// Old comment: _atoi64() has superior performance, so use it when possible.
// Update: Benchmarks in 2018 showed there to be only marginal difference between _ttoi64
// and _tcstoull, with the latter getting slightly better results. Initially _tcstoull
// was used to allow values in the range LLONG_MAX+1..ULLONG_MAX to overflow, but it also
// made the behaviour of larger values even less consistent (producing -1 vs. LLONG_MAX).
// Instead, a custom version of _tcstoi64 is used, allowing all overflow and disallowing
// octal (avoiding the need for IsHex()). This benchmarked considerably faster.
// For example, these expressions are now true:
// 9223372036854775807+1 == 9223372036854775808
// 0xFFFFFFFFFFFFFFFF == -1
// 0x10000000000000001 == 1
//return IsHex(buf) ? _tcstoi64(buf, NULL, 16) : _ttoi64(buf);
return nstrtoi64(buf);
}
inline unsigned __int64 ATOU64(LPCTSTR buf)
{
// Simple type-cast is sufficient since nstrtoi64 doesn't do range checks.
return (unsigned __int64)nstrtoi64(buf);
//return _tcstoui64(buf, NULL, IsHex(buf) ? 16 : 10);
}
inline int ATOI(LPCTSTR buf)
{
// Below has been updated because values with leading zeros were being interpreted as
// octal, which is undesirable.
// Formerly: #define ATOI(buf) strtol(buf, NULL, 0) // Use zero as last param to support both hex & dec.
//return IsHex(buf) ? _tcstol(buf, NULL, 16) : _ttoi(buf); // atoi() has superior performance, so use it when possible.
// Update: ATOI() is mostly used in places where other factors have a much bigger impact
// on performance; still, this method benchmarks slightly faster and produces smaller code
// than the older version above. It is also behaves more consistently with ATOI64() for
// very large out of range values.
return (int)nstrtoi64(buf);
}
// v1.0.38.01: Make ATOU a macro that refers to ATOI64() to improve performance (takes advantage of _atoi64()
// being considerably faster than strtoul(), at least when the number is non-hex). This relies on the fact
// that ATOU() and (UINT)ATOI64() produce the same result due to the way casting works. For example:
// ATOU("-1") == (UINT)ATOI64("-1")
// ATOU("-0xFFFFFFFF") == (UINT)ATOI64("-0xFFFFFFFF")
#define ATOU(buf) (UINT)ATOI64(buf)
//inline unsigned long ATOU(char *buf)
//{
// // As a reminder, strtoul() also handles negative numbers. For example, ATOU("-1") is
// // 4294967295 (0xFFFFFFFF) and ATOU("-2") is 4294967294.
// return strtoul(buf, NULL, IsHex(buf) ? 16 : 10);
//}
// ParseInteger() and ParsePositiveInteger() replace several calls to IsNumeric() that would otherwise
// be immediately followed by a call to ATOI(), ATOI64() or ATOU(). This reduces code size measurably
// in some cases (I observed between 0 and 440 bytes per call) and may also improve performance.
// If buf is a valid integer, sets i to the converted value and returns true.
// Otherwise returns false and leaves i unset.
template<typename T>
bool ParseInteger(LPCTSTR buf, T &i)
{
LPCTSTR endptr;
T temp = static_cast<T>(istrtoi64(buf, &endptr));
if (!*endptr) // No invalid suffix.
{
i = temp; // Set output parameter only now that we know it is valid.
return true;
}
return false;
}
// If buf is a valid integer not starting with '-', sets i to the converted value and returns true.
// Otherwise returns false and leaves i unset.
template<typename T>
bool ParsePositiveInteger(LPCTSTR buf, T &i)
{
// Callers don't want to permit negative values.
return *buf != '-' && ParseInteger(buf, i);
}
inline double ATOF(LPCTSTR buf)
// Unlike some Unix versions of strtod(), the VC++ version does not seem to handle hex strings
// such as "0xFF" automatically. So this macro must check for hex because some callers rely on that.
// Also, it uses _strtoi64() vs. strtol() so that more of a double's capacity can be utilized:
{
return IsHex(buf) ? (double)nstrtoi64(buf) : _tstof(buf);
}
int FTOA(double aValue, LPTSTR aBuf, int aBufSize);
#define ITOA(value, buf) _itot(value, buf, 10)
#define ITOA64(value, buf) _i64tot(value, buf, 10)
#define UTOA(value, buf) _ultot(value, buf, 10)
#define UTOA64(value, buf) _ui64tot(value, buf, 10)
#ifdef _WIN64
#define UPTRTOA UTOA64
#else
#define UPTRTOA UTOA
#endif
//inline LPTSTR HwndToString(HWND aHwnd, LPTSTR aBuf)
//{
// aBuf[0] = '0';
// aBuf[1] = 'x';
// // Use _ultot for performance on 32-bit systems and _ui64tot on 64-bit systems in case it's
// // possible for HWNDs to have non-zero upper 32-bits:
// Exp32or64(_ultot,_ui64tot)((size_t)aHwnd, aBuf + 2, 16);
// return aBuf;
//}
//inline LPTSTR tcscatmove(LPTSTR aDst, LPCTSTR aSrc)
//// Same as strcat() but allows aSrc and aDst to overlap.
//// Unlike strcat(), it doesn't return aDst. Instead, it returns the position
//// in aDst where aSrc was appended.
//{
// if (!aDst || !aSrc || !*aSrc) return aDst;
// LPTSTR aDst_end = aDst + _tcslen(aDst);
// return (LPTSTR)memmove(aDst_end, aSrc, (_tcslen(aSrc) + 1) * sizeof(TCHAR)); // Add 1 to include aSrc's terminator.
//}
// The following macros support StringCaseSense parameters (0, 1, 'Locale'). Locale mode performs
// 1 to 10 times slower for most things, but has the benefit of seeing characters like ä and Ä as identical
// when insensitive. MSDN implies that lstrcmpi() is the same as:
// CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, ...)
// Note that when MSDN talks about the "word sort" vs. "string sort", it does not mean that strings like
// "co-op" and "co-op" are considered equal. Instead, they are considered closer together than the traditional
// string sort would see them, so that they wind up together in a sorted list.
// And both of them benchmark the same, so lstrcmpi is now used here and in various other places throughout
// the program when the new locale-case-insensitive mode is in effect.
#define tcscmp2(str1, str2, string_case_sense) ((string_case_sense) == SCS_INSENSITIVE ? _tcsicmp(str1, str2) \
: ((string_case_sense) == SCS_INSENSITIVE_LOCALE ? lstrcmpi(str1, str2) : _tcscmp(str1, str2)))
// The most common mode is listed first for performance:
#define tcsstr2(haystack, needle, string_case_sense) ((string_case_sense) == SCS_INSENSITIVE ? tcscasestr(haystack, needle) \
: ((string_case_sense) == SCS_INSENSITIVE_LOCALE ? lstrcasestr(haystack, needle) : _tcsstr(haystack, needle)))
// For the following, caller must ensure that len1 and len2 aren't beyond the terminated length of the string
// because CompareString() might not stop at the terminator when a length is specified. Also, CompareString()
// returns 0 on failure, but failure occurs only when parameter/flag is invalid, which should never happen in
// this case.
#define lstrcmpni(str1, len1, str2, len2) (CompareString(LOCALE_USER_DEFAULT, NORM_IGNORECASE, str1, (int)(len1), str2, (int)(len2)) - 2) // -2 for maintainability
// Compare string using the operating system uppercasing table, independent of locale but treating non-ASCII
// letters such as è and È as equivalent. Documentation indicates it will be consistent with NTFS file names.
#define ostrcmpni(str1, len1, str2, len2) (CompareStringOrdinal(str1, (int)(len1), str2, (int)(len2), TRUE) - 2) // -2 for maintainability
#define ostrcmpi(str1, str2) (ostrcmpni(str1, -1, str2, -1))
// The following macros simplify and make consistent the calls to MultiByteToWideChar().
// MSDN implies that passing -1 for cbMultiByte is the most typical and secure usage because it ensures
// that the output is null-terminated: "the resulting wide character string has a null terminator, and the
// length returned by the function includes the terminating null character."
//
// I couldn't find any info on when MB_PRECOMPOSED is needed (if ever). It's the default anyway,
// which implies that passing zero (which is quite common in many examples I've seen) is essentially
// the same as passing MB_PRECOMPOSED. However, some modes such as CP_UTF8 should never use MB_PRECOMPOSED
// or the function will fail.
//
// #1: FROM ANSI TO UNICODE (UTF-16). dest_size_in_wchars includes the terminator.
// From looking at the source to mbstowcs(), it might be faster when the "C" locale is in effect (which is
// the default in the absence of setlocale()) than MultiByteToWideChar() depending on how the latter is
// implemented. This is because mbstowcs() simply casts the characters to (wchar_t)(unsigned char) without
// any other translation at all. Although that behavior is probably identical to MultiByteToWideChar(CP_ACP...),
// it's not completely certain -- so it seems best to stick with MultiByteToWideChar() for consistency
// (also, avoiding mbstowcs slightly reduces code size). If there's ever a case where performance is
// important, create a simple casting loop (see mbstowcs.c for an example) that converts source to dest,
// and test if it performs significantly better than MultiByteToWideChar(CP_ACP...).
#define ToWideChar(source, dest, dest_size_in_wchars) MultiByteToWideChar(CP_ACP, 0, source, -1, dest, dest_size_in_wchars)
//
// #2: FROM UTF-8 TO UNICODE (UTF-16). dest_size_in_wchars includes the terminator. MSDN: "For UTF-8, dwFlags must be set to either 0 or MB_ERR_INVALID_CHARS. Otherwise, the function fails with ERROR_INVALID_FLAGS."
#define UTF8ToWideChar(source, dest, dest_size_in_wchars) MultiByteToWideChar(CP_UTF8, 0, source, -1, dest, dest_size_in_wchars)
//
// #3: FROM UNICODE (UTF-16) TO UTF-8. dest_size_in_bytes includes the terminator.
#define WideCharToUTF8(source, dest, dest_size_in_bytes) WideCharToMultiByte(CP_UTF8, 0, source, -1, dest, dest_size_in_bytes, NULL, NULL)
#define UTF8StrLen(str, cch) MultiByteToWideChar(CP_UTF8, 0, (str), (cch), NULL, 0)
#define WideUTF8StrLen(str, cch) WideCharToMultiByte(CP_UTF8, 0, (str), (cch), NULL, 0, NULL, NULL)
#ifdef UNICODE
#define PosToUTF8Pos WideUTF8StrLen
#define LenToUTF8Len(str,pos,len) WideUTF8StrLen(LPCWSTR(str)+int(pos),len)
#define UTF8PosToPos UTF8StrLen
#define UTF8LenToLen(str,pos,len) UTF8StrLen(LPCSTR(str)+int(pos),len)
inline char* WideToUTF8(LPCWSTR str){
int buf_len = WideCharToUTF8(str, NULL, 0);
LPSTR buf = (LPSTR) malloc(buf_len);
if (buf) WideCharToUTF8(str, buf, buf_len);
return buf;
}
inline LPTSTR UTF8ToWide(LPCSTR str){
int buf_len = UTF8ToWideChar(str, NULL, 0);
LPTSTR buf = (LPTSTR) tmalloc(buf_len);
if (buf) UTF8ToWideChar(str, buf, buf_len);
return buf;
}
#endif
#ifdef UNICODE
#define UorA(u,a) (u)
#define RegExToUTF8(a) CStringUTF8FromTChar(a)
#define TPosToUTF8Pos PosToUTF8Pos
#define TLenToUTF8Len LenToUTF8Len
#define UTF8PosToTPos UTF8PosToPos
#define UTF8LenToTLen UTF8LenToLen
#define ToUnicodeOrAsciiEx(wVirtKey, wScanCode, lpKeyState, pszBuff, wFlags, dwhkl) \
ToUnicodeEx((wVirtKey), (wScanCode), (lpKeyState), (LPWSTR)(pszBuff), 2, (wFlags), (dwhkl))
#else
#define UorA(u,a) (a)
#define RegExToUTF8(a) (a)
#define TPosToUTF8Pos(a,b) (b)
#define TLenToUTF8Len(a,b,c) (c)
#define UTF8PosToTPos(a,b) (b)
#define UTF8LenToTLen(a,b,c) (c)
#define ToUnicodeOrAsciiEx(wVirtKey, wScanCode, lpKeyState, pszBuff, wFlags, dwhkl) \
ToAsciiEx((wVirtKey), (wScanCode), (lpKeyState), (LPWORD)(pszBuff), (wFlags), (dwhkl))
#endif
// v1.0.44.03: Callers now use the following macro rather than the old approach. However, this change
// is meaningful only to people who use more than one keyboard layout. In the case of hotstrings:
// It seems that the vast majority of them would want the Hotstring monitoring to adhere to the active
// window's current keyboard layout rather than the script's. This change is somewhat less certain to
// be desirable unconditionally for the Input command (especially invisible/non-V-option Inputs); but it
// seems best to use the same approach to avoid calling ToAsciiEx() more than once in cases where a
// script has hotstrings and also uses the Input command. Calling ToAsciiEx() twice in such a case would
// be likely to aggravate its side effects with dead keys as described at length in the hook/Input code).
// v1.1.27.01: Retrieve the layout of the thread which owns the focused control, not the active window.
// This fixes UWP apps such as Microsoft Edge, where the top-level window is owned by a different process.
#define Get_active_window_keybd_layout \
HKL active_window_keybd_layout = GetFocusedKeybdLayout();
#define FONT_POINT(hdc, p) (-MulDiv(p, GetDeviceCaps(hdc, LOGPIXELSY), 72))
#define DATE_FORMAT_LENGTH 14 // "YYYYMMDDHHMISS"
#define IS_LEAP_YEAR(year) ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
int GetYDay(int aMon, int aDay, bool aIsLeapYear);
int GetISOWeekNumber(LPTSTR aBuf, int aYear, int aYDay, int aWDay);
ResultType YYYYMMDDToFileTime(LPCTSTR aYYYYMMDD, FILETIME &aFileTime);
DWORD YYYYMMDDToSystemTime2(LPCTSTR aYYYYMMDD, SYSTEMTIME *aSystemTime);
ResultType YYYYMMDDToSystemTime(LPCTSTR aYYYYMMDD, SYSTEMTIME &aSystemTime, bool aValidateTimeValues);
LPTSTR FileTimeToYYYYMMDD(LPTSTR aBuf, FILETIME &aTime, bool aConvertToLocalTime = false);
LPTSTR SystemTimeToYYYYMMDD(LPTSTR aBuf, SYSTEMTIME &aTime);
__int64 YYYYMMDDSecondsUntil(LPCTSTR aYYYYMMDDStart, LPCTSTR aYYYYMMDDEnd, FResult &aStatus);
__int64 FileTimeSecondsUntil(FILETIME *pftStart, FILETIME *pftEnd);
SymbolType IsNumeric(LPCTSTR aBuf, BOOL aAllowNegative = false // BOOL vs. bool might squeeze a little more performance out of this frequently-called function.
, BOOL aAllowAllWhitespace = true, BOOL aAllowFloat = false, BOOL aAllowImpure = false);
void strlcpy(LPSTR aDst, LPCSTR aSrc, size_t aDstSize);
void wcslcpy(LPWSTR aDst, LPCWSTR aSrc, size_t aDstSize);
#ifdef UNICODE
#define tcslcpy wcslcpy
#else
#define tcslcpy strlcpy
#endif
int sntprintf(LPTSTR aBuf, int aBufSize, LPCTSTR aFormat, ...);
int sntprintfcat(LPTSTR aBuf, int aBufSize, LPCTSTR aFormat, ...);
// Not currently used by anything, so commented out to possibly reduce code size:
//int tcslcmp (LPTSTR aBuf1, LPTSTR aBuf2, UINT aLength1 = UINT_MAX, UINT aLength2 = UINT_MAX);
int tcslicmp(LPTSTR aBuf1, LPTSTR aBuf2, size_t aLength1 = -1, size_t aLength2 = -1);
LPTSTR tcsrstr(LPTSTR aStr, size_t aStr_length, LPCTSTR aPattern, StringCaseSenseType aStringCaseSense, int aOccurrence = 1);
LPTSTR ltcschr(LPCTSTR haystack, TCHAR ch);
LPTSTR lstrcasestr(LPCTSTR phaystack, LPCTSTR pneedle);
LPTSTR tcscasestr (LPCTSTR phaystack, LPCTSTR pneedle);
UINT StrReplace(LPTSTR aHaystack, LPTSTR aOld, LPTSTR aNew, StringCaseSenseType aStringCaseSense
, UINT aLimit = UINT_MAX, size_t aSizeLimit = -1, LPTSTR *aDest = NULL, size_t *aHaystackLength = NULL);
size_t PredictReplacementSize(ptrdiff_t aLengthDelta, int aReplacementCount, int aLimit, size_t aHaystackLength
, size_t aCurrentLength, size_t aEndOffsetOfCurrMatch);
LPTSTR TranslateLFtoCRLF(LPCTSTR aString);
bool DoesFilePatternExist(LPCTSTR aFilePattern, DWORD *aFileAttr = NULL, DWORD aRequiredAttr = 0);
LPTSTR ConvertFilespecToCorrectCase(LPTSTR aFilespec, LPTSTR aBuf, size_t aBufSize, size_t &aBufLength);
void ConvertFilespecToCorrectCase(LPTSTR aBuf, size_t aBufSize, size_t &aBufLength);
LPTSTR FileAttribToStr(LPTSTR aBuf, DWORD aAttr);
unsigned __int64 GetFileSize64(HANDLE aFileHandle);
LPTSTR GetWin32ErrorText(LPTSTR aBuf, DWORD aBufSize, DWORD aError);
void AssignColor(LPTSTR aNewColorName, COLORREF &aColor, HBRUSH &aBrush);
void AssignColor(COLORREF aNewColorValue, COLORREF &aColor, HBRUSH &aBrush);
bool ColorToBGR(LPCTSTR aColorNameOrRGB, COLORREF &aBGR);
COLORREF ColorNameToBGR(LPCTSTR aColorName);
LPTSTR ConvertEscapeSequences(LPTSTR aBuf, LPTSTR aLiteralMap);
LPTSTR ConvertSpaceEscapeSequences(LPTSTR aBuf);
int FindExprDelim(LPCTSTR aBuf, TCHAR aDelimiter = ',', int aStartIndex = 0, LPCTSTR aLiteralMap = NULL);
int FindTextDelim(LPCTSTR aBuf, TCHAR aDelimiter = ',', int aStartIndex = 0, LPCTSTR aLiteralMap = NULL);
#define MAX_BALANCEEXPR_DEPTH 100 // The maximum depth at which BalanceExpr() can validate symbols (the size of aExpect[]).
int BalanceExpr(LPCTSTR aBuf, int aStartBalance, TCHAR aExpect[], TCHAR *aOpenQuote = NULL);
POINT CenterWindow(int aWidth, int aHeight);
bool FontExist(HDC aHdc, LPCTSTR aTypeface);
void ScreenToWindow(POINT &aPoint, HWND aHwnd);
void CoordToScreen(int &aX, int &aY, int aWhichMode);
void CoordToScreen(POINT &aPoint, int aWhichMode);
BOOL IsProcess64Bit(HANDLE aHandle);
BOOL IsOS64Bit();
LPVOID AllocInterProcMem(HANDLE &aHandle, DWORD aSize, HWND aHwnd, DWORD aExtraAccess = 0);
void FreeInterProcMem(HANDLE aHandle, LPVOID aMem);
bool ToolTipTextEquals(HWND aToolTipHwnd, LPCTSTR aText);
DWORD GetEnvVarReliable(LPCTSTR aEnvVarName, LPTSTR aBuf);
DWORD ReadRegString(HKEY aRootKey, LPTSTR aSubkey, LPTSTR aValueName, LPTSTR aBuf, DWORD aBufSize, DWORD aFlag = 0);
int GetSystemTrayIconSize();
HBITMAP LoadPicture(LPCTSTR aFilespec, int aWidth, int aHeight, int &aImageType, int aIconNumber
, bool aUseGDIPlusIfAvailable, bool *apNoDelete = NULL, HMODULE *apModule = NULL);
HBITMAP IconToBitmap(HICON ahIcon, bool aDestroyIcon);
HBITMAP IconToBitmap32(HICON aIcon, bool aDestroyIcon); // Lexikos: Used for menu icons on Vista+. Creates a 32-bit (ARGB) device-independent bitmap from an icon.
int CALLBACK FontEnumProc(ENUMLOGFONTEX *lpelfe, NEWTEXTMETRICEX *lpntme, DWORD FontType, LPARAM lParam);
//bool IsStringInList(LPTSTR aStr, LPTSTR aList, bool aFindExactMatch);
LPCTSTR InStrAny(LPCTSTR aStr, LPTSTR aNeedle[], int aNeedleCount, size_t &aFoundLen);
LPTSTR ResourceIndexToId(HMODULE aModule, LPCTSTR aType, int aIndex); // L17: Find integer ID of resource from index. i.e. IconNumber -> resource ID.
HICON ExtractIconFromExecutable(LPCTSTR aFilespec, int aIconNumber, int aWidth, int aHeight // L17: Extract icon of the appropriate size from an executable (or compatible) file.
, HMODULE *apModule = NULL);
PWSTR GetDocumentsFolder();
int VersionSatisfies(LPCTSTR v, LPCTSTR r, bool aThreeWayDefault = false);
int CompareVersion(LPCTSTR a, LPCTSTR b);
BOOLEAN __stdcall GenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
// This is used due to the popcnt instruction not being supported on old CPUs.
// Source: https://www.autohotkey.com/boards/viewtopic.php?f=14&p=384978
inline int popcount8(unsigned char c)
{
c = (c & 0x55u) + ((c >> 1) & 0x55u);
c = (c & 0x33u) + ((c >> 2) & 0x33u);
c = (c & 0x0fu) + ((c >> 4) & 0x0fu);
return c;
}
#if defined(_MSC_VER) && defined(_DEBUG)
void OutputDebugStringFormat(LPCTSTR fmt, ...); // put debug message to the "Output" panel of Visual Studio.
#endif
#endif