stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
string.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2016-2023 Amebis
4*/
5
6#pragma once
7
8#include "compat.hpp"
9#include "locale.hpp"
10#include <ctype.h>
11#include <stdarg.h>
12#include <stdarg.h>
13#include <stdint.h>
14#include <stdio.h>
15#include <time.h>
16#if defined(__APPLE__)
17#include <xlocale.h>
18#endif
19#include <algorithm>
20#include <climits>
21#include <locale>
22#include <stdexcept>
23
24namespace stdex
25{
29#ifdef _WIN32
30 typedef wchar_t utf16_t;
31#else
32 typedef char16_t utf16_t;
33#endif
34
40 inline bool is_high_surrogate(_In_ utf16_t chr)
41 {
42 return 0xd800 < chr && chr < 0xdc00;
43 }
44
50 inline bool is_low_surrogate(_In_ utf16_t chr)
51 {
52 return 0xdc00 < chr && chr < 0xe000;
53 }
54
60 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
61 {
62 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
63 }
64
70 inline char32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
71 {
72 _Assume_(is_surrogate_pair(str));
73 return
74 (static_cast<char32_t>(str[0] - 0xd800) << 10) +
75 static_cast<char32_t>(str[1] - 0xdc00) +
76 0x10000;
77 }
78
84 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ char32_t chr)
85 {
86 _Assume_(chr >= 0x10000);
87 chr -= 0x10000;
88 str[0] = 0xd800 + static_cast<char32_t>((chr >> 10) & 0x3ff);
89 str[1] = 0xdc00 + static_cast<char32_t>(chr & 0x3ff);
90 }
91
97 inline bool iscombining(_In_ char32_t chr)
98 {
99 return
100 (0x0300 <= chr && chr < 0x0370) ||
101 (0x1dc0 <= chr && chr < 0x1e00) ||
102 (0x20d0 <= chr && chr < 0x2100) ||
103 (0xfe20 <= chr && chr < 0xfe30);
104 }
105
111 template <class T>
112 inline bool islbreak(_In_ T chr)
113 {
114 return chr == '\n' || chr == '\r';
115 }
116
125 template <class T>
126 inline size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
127 {
128 _Assume_(chr || !count);
129 if (count >= 2 && ((chr[0] == '\r' && chr[1] == '\n') || (chr[0] == '\n' && chr[1] == '\r')))
130 return 2;
131 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
132 return 1;
133 return 0;
134 }
135
141 template <class T>
142 inline bool isspace(_In_ T chr)
143 {
144 return chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' || chr == '\v' || chr == '\f';
145 }
146
152 template <class T>
153 inline bool islower(_In_ T chr)
154 {
155 return 'a' <= chr && chr <= 'z';
156 }
157
163 template <class T>
164 inline bool isupper(_In_ T chr)
165 {
166 return 'A' <= chr && chr <= 'Z';
167 }
168
174 template <class T>
175 inline bool isdigit(_In_ T chr)
176 {
177 return '0' <= chr && chr <= '9';
178 }
179
185 template <class T>
186 inline bool isalpha(_In_ T chr)
187 {
188 return islower(chr) || isupper(chr);
189 }
190
196 template <class T>
197 inline bool is7bit(_In_ T chr)
198 {
199 return '\x00' <= chr && chr <= '\x7f';
200 }
201
208 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
209 {
210 _Assume_(glyph || !count);
211 if (count) {
212#ifdef _WIN32
213 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
214#else
215 size_t i = 1;
216#endif
217 for (; i < count && iscombining(glyph[i]); ++i);
218 return i;
219 }
220 return 0;
221 }
222
229 inline size_t glyphrlen(_In_reads_or_z_opt_(count) const wchar_t* str, _In_ size_t count)
230 {
231 _Assume_(count && str && str[count - 1]);
232 for (size_t i = count; i--;) {
233 if (!iscombining(str[i])) {
234#ifdef _WIN32
235 return count - (!is_low_surrogate(str[i]) || i == 0 || !is_high_surrogate(str[i - 1]) ? i : i - 1);
236#else
237 return count - i;
238#endif
239 }
240 }
241 return count;
242 }
243
251 template <class T>
252 inline T tolower(_In_ T chr)
253 {
254 return isupper(chr) ? chr | 0x20 : chr;
255 }
256
264 template <class T>
265 inline T toupper(_In_ T chr)
266 {
267 return islower(chr) ? chr | ~0x20 : chr;
268 }
269
277 template <class T>
278 inline size_t strlen(_In_z_ const T* str)
279 {
280 _Assume_(str);
281 size_t i;
282 for (i = 0; str[i]; ++i);
283 return i;
284 }
285
294 template <class T>
295 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
296 {
297 _Assume_(str || !count);
298 size_t i;
299 for (i = 0; i < count && str[i]; ++i);
300 return i;
301 }
302
310 template <class T, size_t N>
311 inline size_t strnlen(_In_ const T (&str)[N])
312 {
313 return strnlen(str, N);
314 }
315
316 constexpr auto npos{ static_cast<size_t>(-1) };
317
326 template <class T>
327 inline size_t strchr(_In_z_ const T* str, _In_ T chr)
328 {
329 _Assume_(str);
330 for (size_t i = 0; str[i]; ++i)
331 if (str[i] == chr) return i;
332 return npos;
333 }
334
344 template <class T>
345 inline size_t strnchr(
346 _In_reads_or_z_opt_(count) const T* str,
347 _In_ size_t count,
348 _In_ T chr)
349 {
350 _Assume_(str || !count);
351 for (size_t i = 0; i < count && str[i]; ++i)
352 if (str[i] == chr) return i;
353 return npos;
354 }
355
364 template <class T, size_t N>
365 inline size_t strnchr(
366 _In_ const T (&str)[N],
367 _In_ T chr)
368 {
369 return strnchr(str, N, chr);
370 }
371
380 template <class T>
381 inline size_t strrchr(
382 _In_z_ const T* str,
383 _In_ T chr)
384 {
385 _Assume_(str);
386 size_t z = npos;
387 for (size_t i = 0; str[i]; ++i)
388 if (str[i] == chr) z = i;
389 return z;
390 }
391
401 template <class T>
402 inline size_t strrnchr(
403 _In_reads_or_z_opt_(count) const T* str,
404 _In_ size_t count,
405 _In_ T chr)
406 {
407 _Assume_(str || !count);
408 size_t z = npos;
409 for (size_t i = 0; i < count && str[i]; ++i)
410 if (str[i] == chr) z = i;
411 return z;
412 }
413
422 template <class T, size_t N>
423 inline size_t strrnchr(
424 _In_ const T (&str)[N],
425 _In_ T chr)
426 {
427 return strrnchr(str, N, chr);
428 }
429
438 template <class T>
439 inline size_t strichr(
440 _In_z_ const T* str,
441 _In_ T chr)
442 {
443 _Assume_(str);
444 chr = tolower(chr);
445 for (size_t i = 0; str[i]; ++i)
446 if (tolower(str[i]) == chr) return i;
447 return npos;
448 }
449
459 template <class T>
460 inline size_t strichr(
461 _In_z_ const T* str,
462 _In_ T chr,
463 _In_ const std::locale& locale)
464 {
465 _Assume_(str);
466 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
467 chr = ctype.tolower(chr);
468 for (size_t i = 0; str[i]; ++i)
469 if (ctype.tolower(str[i]) == chr) return i;
470 return npos;
471 }
472
482 template <class T>
483 inline size_t strnichr(
484 _In_reads_or_z_opt_(count) const T* str,
485 _In_ size_t count,
486 _In_ T chr)
487 {
488 _Assume_(str || !count);
489 chr = tolower(chr);
490 for (size_t i = 0; i < count && str[i]; ++i)
491 if (tolower(str[i]) == chr) return i;
492 return npos;
493 }
494
505 template <class T>
506 inline size_t strnichr(
507 _In_reads_or_z_opt_(count) const T* str,
508 _In_ size_t count,
509 _In_ T chr,
510 _In_ const std::locale& locale)
511 {
512 _Assume_(str || !count);
513 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
514 chr = ctype.tolower(chr);
515 for (size_t i = 0; i < count && str[i]; ++i)
516 if (ctype.tolower(str[i]) == chr) return i;
517 return npos;
518 }
519
528 template <class T, size_t N>
529 inline size_t strnichr(
530 _In_ const T (&str)[N],
531 _In_ T chr)
532 {
533 return strnichr(str, N, chr);
534 }
535
545 template <class T, size_t N>
546 inline size_t strnichr(
547 _In_ const T (&str)[N],
548 _In_ T chr,
549 _In_ const std::locale& locale)
550 {
551 return strnichr(str, N, chr, locale);
552 }
553
562 template <class T>
563 inline size_t strrichr(
564 _In_z_ const T* str,
565 _In_ T chr)
566 {
567 _Assume_(str);
568 chr = tolower(chr);
569 size_t z = npos;
570 for (size_t i = 0; str[i]; ++i)
571 if (tolower(str[i]) == chr) z = i;
572 return z;
573 }
574
584 template <class T>
585 inline size_t strrichr(
586 _In_reads_or_z_opt_(count) const T* str,
587 _In_ T chr,
588 _In_ const std::locale& locale)
589 {
590 _Assume_(str);
591 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
592 chr = ctype.tolower(chr);
593 size_t z = npos;
594 for (size_t i = 0; str[i]; ++i)
595 if (ctype.tolower(str[i]) == chr) z = i;
596 return z;
597 }
598
608 template <class T>
609 inline size_t strrnichr(
610 _In_reads_or_z_opt_(count) const T* str,
611 _In_ size_t count,
612 _In_ T chr)
613 {
614 _Assume_(str || !count);
615 chr = tolower(chr);
616 size_t z = npos;
617 for (size_t i = 0; i < count && str[i]; ++i)
618 if (tolower(str[i]) == chr) z = i;
619 return z;
620 }
621
632 template <class T>
633 inline size_t strrnichr(
634 _In_reads_or_z_opt_(count) const T* str,
635 _In_ size_t count,
636 _In_ T chr,
637 _In_ const std::locale& locale)
638 {
639 _Assume_(str || !count);
640 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
641 chr = ctype.tolower(chr);
642 size_t z = npos;
643 for (size_t i = 0; i < count && str[i]; ++i)
644 if (ctype.tolower(str[i]) == chr) z = i;
645 return z;
646 }
647
656 template <class T, size_t N>
657 inline size_t strrnichr(
658 _In_ const T (&str)[N],
659 _In_ T chr)
660 {
661 return strrnichr(str, N, chr);
662 }
663
673 template <class T, size_t N>
674 inline size_t strrnichr(
675 _In_ const T (&str)[N],
676 _In_ T chr,
677 _In_ const std::locale& locale)
678 {
679 return strrnichr(str, N, chr, locale);
680 }
681
689 //template <class T>
690 //inline bool isblank(_In_z_ const T* str)
691 //{
692 // _Assume_(str);
693 // for (size_t i = 0; str[i]; ++i)
694 // if (!isspace(str[i]))
695 // return false;
696 // return true;
697 //}
698
707 //template <class T>
708 //inline bool isblank(
709 // _In_z_ const T* str,
710 // _In_ const std::locale& locale)
711 //{
712 // _Assume_(str);
713 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
714 // for (size_t i = 0; str[i]; ++i)
715 // if (!ctype.is(ctype.space, str[i]))
716 // return false;
717 // return true;
718 //}
719
728 template <class T>
729 inline bool isblank(
730 _In_reads_or_z_opt_(count) const T* str,
731 _In_ size_t count)
732 {
733 _Assume_(str || !count);
734 for (size_t i = 0; i < count && str[i]; ++i)
735 if (!isspace(str[i]))
736 return false;
737 return true;
738 }
739
749 template <class T>
750 inline bool isblank(
751 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
752 _In_ const std::locale& locale)
753 {
754 _Assume_(str || !count);
755 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
756 for (size_t i = 0; i < count && str[i]; ++i)
757 if (!ctype.is(ctype.space, str[i]))
758 return false;
759 return true;
760 }
761
769 template <class T, size_t N>
770 inline bool isblank(_In_ const T (&str)[N])
771 {
772 return isblank(str, N);
773 }
774
783 template <class T, size_t N>
784 inline bool isblank(
785 _In_ const T (&str)[N],
786 _In_ const std::locale& locale)
787 {
788 return isblank(str, N, locale);
789 }
790
791 // ///
792 // /// Checks if string contains all-ASCII characters
793 // ///
794 // /// \param[in] str String
795 // ///
796 // /// \return `true` if all characters are ASCII or `false` when any non-ASCII character is found in string.
797 // ///
798 // template <class T>
799 // inline bool is7bit(_In_z_ const T* str)
800 // {
801 // _Assume_(str);
802 // for (size_t i = 0; str[i]; i++)
803 // if (!is7bit(str[i]))
804 // return false;
805 // return true;
806 // }
807
816 template <class T>
817 inline bool is7bit(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
818 {
819 _Assume_(str || !count);
820 for (size_t i = 0; i < count && str[i]; i++)
821 if (!is7bit(str[i]))
822 return false;
823 return true;
824 }
825
833 template <class T, size_t N>
834 inline bool is7bit(_In_ const T (&str)[N])
835 {
836 return is7bit(str, N);
837 }
838
847 template <class T1, class T2>
848 inline int strcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
849 {
850 _Assume_(str1);
851 _Assume_(str2);
852 size_t i; T1 a; T2 b;
853 for (i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
854 if (a > b) return +1;
855 if (a < b) return -1;
856 }
857 if (str1[i]) return +1;
858 if (str2[i]) return -1;
859 return 0;
860 }
861
871 template <class T1, class T2>
872 inline int strncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
873 {
874 _Assume_(str1 || !count);
875 _Assume_(str2 || !count);
876 size_t i; T1 a; T2 b;
877 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
878 if (a > b) return +1;
879 if (a < b) return -1;
880 }
881 if (i < count && str1[i]) return +1;
882 if (i < count && str2[i]) return -1;
883 return 0;
884 }
885
896 template <class T1, class T2>
897 inline int strncmp(
898 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
899 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
900 {
901 _Assume_(str1 || !count1);
902 _Assume_(str2 || !count2);
903 size_t i; T1 a; T2 b;
904 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
905 if (a > b) return +1;
906 if (a < b) return -1;
907 }
908 if (i < count1 && str1[i]) return +1;
909 if (i < count2 && str2[i]) return -1;
910 return 0;
911 }
912
921 template <class T1, size_t N1, class T2, size_t N2>
922 inline int strncmp(
923 _In_ const T1 (&str1)[N1],
924 _In_ const T2 (&str2)[N2])
925 {
926 return strncmp(str1, N1, str2, N2);
927 }
928
937 template <class T1, class T2>
938 inline int strrcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
939 {
940 size_t
941 i = strlen(str1),
942 j = strlen(str2);
943 _Assume_(str1 || !i);
944 _Assume_(str2 || !j);
945 size_t k; T1 a; T2 b;
946 for (k = 1; i && j; k++) {
947 i--; j--;
948 if ((a = str1[i]) > (b = str2[j])) return +1;
949 if (a < b) return -1;
950 }
951 if (i && !j) return +1;
952 if (!i && j) return -1;
953 return 0;
954 }
955
965 template <class T1, class T2>
966 inline int strrncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
967 {
968 size_t
969 i = strnlen(str1, count),
970 j = strnlen(str2, count);
971 _Assume_(str1 || !i);
972 _Assume_(str2 || !j);
973 size_t k; T1 a; T2 b;
974 for (k = 1; i && j; k++) {
975 i--; j--;
976 if ((a = str1[i]) > (b = str2[j])) return +1;
977 if (a < b) return -1;
978 }
979 if (i && !j) return +1;
980 if (!i && j) return -1;
981 return 0;
982 }
983
994 template <class T1, class T2>
995 inline int strrncmp(
996 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
997 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
998 {
999 size_t
1000 i = strnlen(str1, count1),
1001 j = strnlen(str2, count2);
1002 _Assume_(str1 || !i);
1003 _Assume_(str2 || !j);
1004 size_t k; T1 a; T2 b;
1005 for (k = 1; i && j; k++) {
1006 i--; j--;
1007 if ((a = str1[i]) > (b = str2[j])) return +1;
1008 if (a < b) return -1;
1009 }
1010 if (i && !j) return +1;
1011 if (!i && j) return -1;
1012 return 0;
1013 }
1014
1023 template <class T1, size_t N1, class T2, size_t N2>
1024 inline int strrncmp(
1025 _In_ const T1 (&str1)[N1],
1026 _In_ const T2 (&str2)[N2])
1027 {
1028 return strrncmp(str1, N1, str2, N2);
1029 }
1030
1039 template <class T1, class T2>
1040 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
1041 {
1042 _Assume_(str1);
1043 _Assume_(str2);
1044 size_t i; T1 a; T2 b;
1045 for (i = 0; (a = tolower(str1[i])) | (b = tolower(str2[i])); ++i) {
1046 if (a > b) return +1;
1047 if (a < b) return -1;
1048 }
1049 if (str1[i]) return +1;
1050 if (str2[i]) return -1;
1051 return 0;
1052 }
1053
1063 template <class T1, class T2>
1064 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
1065 {
1066 _Assume_(str1);
1067 _Assume_(str2);
1068 size_t i; T1 a; T2 b;
1069 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1070 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1071 for (i = 0; (a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i])); ++i) {
1072 if (a > b) return +1;
1073 if (a < b) return -1;
1074 }
1075 if (str1[i]) return +1;
1076 if (str2[i]) return -1;
1077 return 0;
1078 }
1079
1089 template <class T1, class T2>
1090 inline int strnicmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
1091 {
1092 _Assume_(str1 || !count);
1093 _Assume_(str2 || !count);
1094 size_t i; T1 a; T2 b;
1095 for (i = 0; i < count && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); ++i) {
1096 if (a > b) return +1;
1097 if (a < b) return -1;
1098 }
1099 if (i < count && str1[i]) return +1;
1100 if (i < count && str2[i]) return -1;
1101 return 0;
1102 }
1103
1114 template <class T1, class T2>
1115 inline int strnicmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count, _In_ const std::locale& locale)
1116 {
1117 _Assume_(str1 || !count);
1118 _Assume_(str2 || !count);
1119 size_t i; T1 a; T2 b;
1120 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1121 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1122 for (i = 0; i < count && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); ++i) {
1123 if (a > b) return +1;
1124 if (a < b) return -1;
1125 }
1126 if (i < count && str1[i]) return +1;
1127 if (i < count && str2[i]) return -1;
1128 return 0;
1129 }
1130
1141 template <class T1, class T2>
1142 inline int strnicmp(
1143 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1144 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1145 {
1146 _Assume_(str1 || !count1);
1147 _Assume_(str2 || !count2);
1148 size_t i; T1 a; T2 b;
1149 for (i = 0; i < count1 && i < count2 && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); ++i) {
1150 if (a > b) return +1;
1151 if (a < b) return -1;
1152 }
1153 if (i < count1 && str1[i]) return +1;
1154 if (i < count2 && str2[i]) return -1;
1155 return 0;
1156 }
1157
1169 template <class T1, class T2>
1170 inline int strnicmp(
1171 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1172 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
1173 _In_ const std::locale& locale)
1174 {
1175 _Assume_(str1 || !count1);
1176 _Assume_(str2 || !count2);
1177 size_t i; T1 a; T2 b;
1178 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1179 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1180 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); ++i) {
1181 if (a > b) return +1;
1182 if (a < b) return -1;
1183 }
1184 if (i < count1 && str1[i]) return +1;
1185 if (i < count2 && str2[i]) return -1;
1186 return 0;
1187 }
1188
1197 template <class T1, size_t N1, class T2, size_t N2>
1198 inline int strnicmp(
1199 _In_ const T1 (&str1)[N1],
1200 _In_ const T2 (&str2)[N2])
1201 {
1202 strnicmp(str1, N1, str2, N2);
1203 }
1204
1214 template <class T1, size_t N1, class T2, size_t N2>
1215 inline int strnicmp(
1216 _In_ const T1 (&str1)[N1],
1217 _In_ const T2 (&str2)[N2],
1218 _In_ const std::locale& locale)
1219 {
1220 strnicmp(str1, N1, str2, N2, locale);
1221 }
1222
1232 template <class T>
1233 inline int strcoll(
1234 _In_z_ const T* str1,
1235 _In_z_ const T* str2,
1236 _In_ const std::locale& locale)
1237 {
1238 _Assume_(str1);
1239 _Assume_(str2);
1240 auto& collate = std::use_facet<std::collate<T>>(locale);
1241 return collate.compare(str1, str1 + strlen(str1), str2, str2 + strlen(str2));
1242 }
1243
1255 template <class T>
1256 inline int strncoll(
1257 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
1258 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
1259 _In_ const std::locale& locale)
1260 {
1261 _Assume_(str1 || !count1);
1262 _Assume_(str2 || !count2);
1263 auto& collate = std::use_facet<std::collate<T>>(locale);
1264 return collate.compare(str1, str1 + count1, str2, str2 + count2);
1265 }
1266
1276 template <class T, size_t N1, size_t N2>
1277 inline int strncoll(
1278 _In_ const T (&str1)[N1],
1279 _In_ const T (&str2)[N2],
1280 _In_ const std::locale& locale)
1281 {
1282 return strncoll(str1, N1, str2, N2, locale);
1283 }
1284
1293 template <class T1, class T2>
1294 inline size_t strstr(
1295 _In_z_ const T1* str,
1296 _In_z_ const T2* sample)
1297 {
1298 _Assume_(str);
1299 _Assume_(sample);
1300 for (size_t offset = 0;; ++offset) {
1301 for (size_t i = offset, j = 0;; ++i, ++j) {
1302 if (!sample[j])
1303 return offset;
1304 if (!str[i])
1305 return npos;
1306 if (str[i] != sample[j])
1307 break;
1308 }
1309 }
1310 }
1311
1321 template <class T1, class T2>
1322 inline size_t strnstr(
1323 _In_reads_or_z_opt_(count) const T1* str, _In_ size_t count,
1324 _In_z_ const T2* sample)
1325 {
1326 _Assume_(str || !count);
1327 _Assume_(sample);
1328 for (size_t offset = 0;; ++offset) {
1329 for (size_t i = offset, j = 0;; ++i, ++j) {
1330 if (!sample[j])
1331 return offset;
1332 if (i >= count || !str[i])
1333 return npos;
1334 if (str[i] != sample[j])
1335 break;
1336 }
1337 }
1338 }
1339
1348 template <class T1, size_t N1, class T2>
1349 inline size_t strnstr(
1350 _In_ const T1 (&str)[N1],
1351 _In_z_ const T2* sample)
1352 {
1353 return strnstr(str, N1, sample);
1354 }
1355
1364 template <class T1, class T2>
1365 inline size_t stristr(
1366 _In_z_ const T1* str,
1367 _In_z_ const T2* sample)
1368 {
1369 _Assume_(str);
1370 _Assume_(sample);
1371 for (size_t offset = 0;; ++offset) {
1372 for (size_t i = offset, j = 0;; ++i, ++j) {
1373 if (!sample[j])
1374 return offset;
1375 if (!str[i])
1376 return npos;
1377 if (tolower(str[i]) != tolower(sample[j]))
1378 break;
1379 }
1380 }
1381 }
1382
1392 template <class T1, class T2>
1393 inline size_t stristr(
1394 _In_z_ const T1* str,
1395 _In_z_ const T2* sample,
1396 _In_ const std::locale& locale)
1397 {
1398 _Assume_(str);
1399 _Assume_(sample);
1400 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1401 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1402 for (size_t offset = 0;; ++offset) {
1403 for (size_t i = offset, j = 0;; ++i, ++j) {
1404 if (!sample[j])
1405 return offset;
1406 if (!str[i])
1407 return npos;
1408 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1409 break;
1410 }
1411 }
1412 }
1413
1423 template <class T1, class T2>
1424 inline size_t strnistr(
1425 _In_reads_or_z_opt_(count) const T1* str,
1426 _In_ size_t count,
1427 _In_z_ const T2* sample)
1428 {
1429 _Assume_(str || !count);
1430 _Assume_(sample);
1431 for (size_t offset = 0;; ++offset) {
1432 for (size_t i = offset, j = 0;; ++i, ++j) {
1433 if (!sample[j])
1434 return offset;
1435 if (i >= count || !str[i])
1436 return npos;
1437 if (tolower(str[i]) != tolower(sample[j]))
1438 break;
1439 }
1440 }
1441 }
1442
1453 template <class T1, class T2>
1454 inline size_t strnistr(
1455 _In_reads_or_z_opt_(count) const T1* str,
1456 _In_ size_t count,
1457 _In_z_ const T2* sample,
1458 _In_ const std::locale& locale)
1459 {
1460 _Assume_(str || !count);
1461 _Assume_(sample);
1462 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1463 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1464 for (size_t offset = 0;; ++offset) {
1465 for (size_t i = offset, j = 0;; ++i, ++j) {
1466 if (!sample[j])
1467 return offset;
1468 if (i >= count || !str[i])
1469 return npos;
1470 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1471 break;
1472 }
1473 }
1474 }
1475
1484 template <class T1, size_t N1, class T2>
1485 inline size_t strnistr(
1486 _In_ const T1 (&str)[N1],
1487 _In_z_ const T2* sample)
1488 {
1489 return strnistr(str, N1, sample);
1490 }
1491
1501 template <class T1, size_t N1, class T2>
1502 inline size_t strnistr(
1503 _In_ const T1 (&str)[N1],
1504 _In_z_ const T2* sample,
1505 _In_ const std::locale& locale)
1506 {
1507 return strnistr(str, N1, sample, locale);
1508 }
1509
1518 template <class T1, class T2>
1519 inline size_t strcpy(
1520 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
1521 _In_z_ const T2* src)
1522 {
1523 _Assume_(dst);
1524 _Assume_(src);
1525 for (size_t i = 0; ; ++i) {
1526 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1527 return i;
1528 }
1529 }
1530
1540 template <class T1, class T2>
1541 inline size_t strncpy(
1542 _Out_writes_(count) _Post_maybez_ T1* dst,
1543 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1544 {
1545 _Assume_(dst || !count);
1546 _Assume_(src || !count);
1547 for (size_t i = 0; ; ++i) {
1548 if (i >= count)
1549 return i;
1550 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1551 return i;
1552 }
1553 }
1554
1565 template <class T1, class T2>
1566 inline size_t strncpy(
1567 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1568 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1569 {
1570 _Assume_(dst || !count_dst);
1571 _Assume_(src || !count_src);
1572 for (size_t i = 0; ; ++i)
1573 {
1574 if (i >= count_dst)
1575 return i;
1576 if (i >= count_src) {
1577 dst[i] = 0;
1578 return i;
1579 }
1580 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1581 return i;
1582 }
1583 }
1584
1593 template <class T1, size_t N1, class T2, size_t N2>
1594 inline size_t strncpy(
1595 _Out_ _Post_maybez_ T1 (&dst)[N1],
1596 _In_ const T2 (&src)[N2])
1597 {
1598 return strncpy(dst, N1, src, N2);
1599 }
1600
1609 template <class T1, class T2>
1610 inline size_t strcat(
1611 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
1612 _In_z_ const T2* src)
1613 {
1614 _Assume_(dst);
1615 _Assume_(src);
1616 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1617 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1618 return j;
1619 }
1620 }
1621
1631 template <class T1, class T2>
1632 inline size_t strncat(
1633 _Inout_z_ T1* dst,
1634 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1635 {
1636 _Assume_(dst || !count);
1637 _Assume_(src || !count);
1638 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1639 if (i >= count)
1640 return j;
1641 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1642 return j;
1643 }
1644 }
1645
1656 template <class T1, class T2>
1657 inline size_t strncat(
1658 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1659 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1660 {
1661 _Assume_(dst || !count_dst);
1662 _Assume_(src || !count_src);
1663 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
1664 {
1665 if (j >= count_dst)
1666 return j;
1667 if (i >= count_src) {
1668 dst[j] = 0;
1669 return j;
1670 }
1671 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1672 return j;
1673 }
1674 }
1675
1686 template <class T>
1687 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
1688 {
1689 if (!str) _Unlikely_
1690 return nullptr;
1691 size_t count = strlen(str) + 1;
1692 T* dst = new T[count];
1693 strncpy(dst, count, str, SIZE_MAX);
1694 return dst;
1695 }
1696
1708 template <class T>
1709 inline _Ret_z_ T* strndup(
1710 _In_reads_or_z_opt_(count) const T* str,
1711 _In_ size_t count)
1712 {
1713 T* dst = new T[count];
1714 strncpy(dst, count, str, SIZE_MAX);
1715 return dst;
1716 }
1717
1728 template <class T, size_t N>
1729 inline _Check_return_ _Ret_maybenull_z_ T* strndup(_In_ const T (&str)[N])
1730 {
1731 return strndup(str, N);
1732 }
1733
1743 template <class T>
1744 inline size_t crlf2nl(_Out_writes_z_(_String_length_(src) + 1) T* dst, _In_z_ const T* src)
1745 {
1746 _Assume_(dst);
1747 _Assume_(src);
1748 size_t i, j;
1749 for (i = j = 0; src[j];) {
1750 if (src[j] != '\r' || src[j + 1] != '\n')
1751 dst[i++] = src[j++];
1752 else {
1753 dst[i++] = '\n';
1754 j += 2;
1755 }
1756 }
1757 dst[i] = 0;
1758 return i;
1759 }
1760
1767 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1768 inline void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& dst, _In_z_ const T* src)
1769 {
1770 _Assume_(src);
1771 _Assume_(src != dst.data());
1772 dst.clear();
1773 dst.reserve(strlen(src));
1774 for (size_t j = 0; src[j];) {
1775 if (src[j] != '\r' || src[j + 1] != '\n')
1776 dst += src[j++];
1777 else {
1778 dst += '\n';
1779 j += 2;
1780 }
1781 }
1782 }
1783
1789 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1790 inline void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& str)
1791 {
1792 size_t i, j, n;
1793 for (i = j = 0, n = str.size(); j < n;) {
1794 if (str[j] != '\r' || str[j + 1] != '\n')
1795 str[i++] = str[j++];
1796 else {
1797 str[i++] = '\n';
1798 j += 2;
1799 }
1800 }
1801 str.resize(i);
1802 }
1803
1805 template <class T, class T_bin>
1806 inline T_bin strtoint(
1807 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1808 _Out_opt_ size_t* end,
1809 _In_ int radix,
1810 _Out_ uint8_t& flags)
1811 {
1812 _Assume_(str || !count);
1813 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
1814
1815 size_t i = 0;
1816 T_bin value = 0, digit,
1817 max_ui = (T_bin)-1,
1818 max_ui_pre1, max_ui_pre2;
1819
1820 flags = 0;
1821
1822 // Skip leading spaces.
1823 for (;; ++i) {
1824 if (i >= count || !str[i]) goto error;
1825 if (!isspace(str[i])) break;
1826 }
1827
1828 // Read the sign.
1829 if (str[i] == '+') {
1830 flags &= ~0x01;
1831 ++i;
1832 if (i >= count || !str[i]) goto error;
1833 }
1834 else if (str[i] == '-') {
1835 flags |= 0x01;
1836 ++i;
1837 if (i >= count || !str[i]) goto error;
1838 }
1839
1840 if (radix == 16) {
1841 // On hexadecimal, allow leading 0x.
1842 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
1843 i += 2;
1844 if (i >= count || !str[i]) goto error;
1845 }
1846 }
1847 else if (!radix) {
1848 // Autodetect radix.
1849 if (str[i] == '0') {
1850 ++i;
1851 if (i >= count || !str[i]) goto error;
1852 if (str[i] == 'x' || str[i] == 'X') {
1853 radix = 16;
1854 ++i;
1855 if (i >= count || !str[i]) goto error;
1856 }
1857 else
1858 radix = 8;
1859 }
1860 else
1861 radix = 10;
1862 }
1863
1864 // We have the radix.
1865 max_ui_pre1 = max_ui / (T_bin)radix;
1866 max_ui_pre2 = max_ui % (T_bin)radix;
1867 for (;;) {
1868 if ('0' <= str[i] && str[i] <= '9')
1869 digit = (T_bin)str[i] - '0';
1870 else if ('A' <= str[i] && str[i] <= 'Z')
1871 digit = (T_bin)str[i] - 'A' + '\x0a';
1872 else if ('a' <= str[i] && str[i] <= 'z')
1873 digit = (T_bin)str[i] - 'a' + '\x0a';
1874 else
1875 goto error;
1876 if (digit >= (T_bin)radix)
1877 goto error;
1878
1879 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
1880 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
1881 value = value * (T_bin)radix + digit;
1882 else {
1883 // Overflow!
1884 flags |= 0x02;
1885 }
1886
1887 ++i;
1888 if (i >= count || !str[i])
1889 goto error;
1890 }
1891
1892 error:
1893 if (end) *end = i;
1894 return value;
1895 }
1897
1908 template <class T, class T_bin>
1909 T_bin strtoint(
1910 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1911 _Out_opt_ size_t* end,
1912 _In_ int radix)
1913 {
1914 uint8_t flags;
1915 T_bin value;
1916
1917 switch (sizeof(T_bin)) {
1918 case 1:
1919 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
1920 if ((flags & 0x01) && (value & 0x80)) {
1921 // Sign bit is 1 => overflow.
1922 flags |= 0x02;
1923 }
1924 return (flags & 0x02) ?
1925 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
1926 (flags & 0x01) ? -value : value;
1927
1928 case 2:
1929 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
1930 if ((flags & 0x01) && (value & 0x8000)) {
1931 // Sign bit is 1 => overflow.
1932 flags |= 0x02;
1933 }
1934 return (flags & 0x02) ?
1935 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
1936 (flags & 0x01) ? -value : value;
1937
1938 case 4:
1939 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
1940 if ((flags & 0x01) && (value & 0x80000000)) {
1941 // Sign bit is 1 => overflow.
1942 flags |= 0x02;
1943 }
1944 return (flags & 0x02) ?
1945 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
1946 (flags & 0x01) ? -value : value;
1947
1948 case 8:
1949 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
1950 if ((flags & 0x01) && (value & 0x8000000000000000)) {
1951 // Sign bit is 1 => overflow.
1952 flags |= 0x02;
1953 }
1954 return (flags & 0x02) ?
1955 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
1956 (flags & 0x01) ? -value : value;
1957
1958 default:
1959 throw std::invalid_argument("Unsupported bit length");
1960 }
1961 }
1962
1972 template <class T, size_t N, class T_bin>
1973 T_bin strtoint(
1974 _In_ const T (&str)[N],
1975 _Out_opt_ size_t* end,
1976 _In_ int radix)
1977 {
1978 return strtoint<T, T_bin>(str, N, end, radix);
1979 }
1980
1991 template <class T, class T_bin>
1992 inline T_bin strtouint(
1993 _In_reads_or_z_opt_(count) const T* str,
1994 _In_ size_t count,
1995 _Out_opt_ size_t* end,
1996 _In_ int radix)
1997 {
1998 uint8_t flags;
1999 T_bin value;
2000
2001 switch (sizeof(T_bin)) {
2002 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
2003 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
2004 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
2005 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
2006 default: throw std::invalid_argument("Unsupported bit length");
2007 }
2008
2009 return (flags & 0x02) ?
2010 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
2011 (flags & 0x01) ? ~value : value;
2012 }
2013
2023 template <class T, size_t N, class T_bin>
2024 inline T_bin strtouint(
2025 _In_ const T (&str)[N],
2026 _Out_opt_ size_t* end,
2027 _In_ int radix)
2028 {
2029 return strtouint<T, T_bin>(str, N, end, radix);
2030 }
2031
2042 template <class T>
2043 inline int32_t strto32(
2044 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2045 _Out_opt_ size_t* end,
2046 _In_ int radix)
2047 {
2048 return strtoint<T, int32_t>(str, count, end, radix);
2049 }
2050
2060 template <class T, size_t N>
2061 inline int32_t strto32(
2062 _In_ const T (&str)[N],
2063 _Out_opt_ size_t* end,
2064 _In_ int radix)
2065 {
2066 return strto32<T>(str, N, end, radix);
2067 }
2068
2079 template <class T>
2080 inline int64_t strto64(
2081 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2082 _Out_opt_ size_t* end,
2083 _In_ int radix)
2084 {
2085 return strtoint<T, int64_t>(str, count, end, radix);
2086 }
2087
2097 template <class T, size_t N>
2098 inline int64_t strto64(
2099 _In_ const T (&str)[N],
2100 _Out_opt_ size_t* end,
2101 _In_ int radix)
2102 {
2103 return strto64<T>(str, N, end, radix);
2104 }
2105
2117 template <class T>
2118 inline ptrdiff_t strtoi(
2119 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2120 _Out_opt_ size_t* end,
2121 _In_ int radix)
2122 {
2123#if defined(_WIN64) || defined(__LP64__)
2124 return static_cast<ptrdiff_t>(strto64(str, count, end, radix));
2125#else
2126 return static_cast<ptrdiff_t>(strto32(str, count, end, radix));
2127#endif
2128 }
2129
2140 template <class T, size_t N>
2141 inline ptrdiff_t strtoi(
2142 _In_ const T (&str)[N],
2143 _Out_opt_ size_t* end,
2144 _In_ int radix)
2145 {
2146 return strtoi<T>(str, N, end, radix);
2147 }
2148
2159 template <class T>
2160 inline uint32_t strtou32(
2161 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2162 _Out_opt_ size_t* end,
2163 _In_ int radix)
2164 {
2165 return strtouint<T, uint32_t>(str, count, end, radix);
2166 }
2167
2177 template <class T, size_t N>
2178 inline uint32_t strtou32(
2179 _In_ const T (&str)[N],
2180 _Out_opt_ size_t* end,
2181 _In_ int radix)
2182 {
2183 return strtou32(str, N, end, radix);
2184 }
2185
2196 template <class T>
2197 inline uint64_t strtou64(
2198 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2199 _Out_opt_ size_t* end,
2200 _In_ int radix)
2201 {
2202 return strtouint<T, uint64_t>(str, count, end, radix);
2203 }
2204
2214 template <class T, size_t N>
2215 inline uint64_t strtou64(
2216 _In_ const T (&str)[N],
2217 _Out_opt_ size_t* end,
2218 _In_ int radix)
2219 {
2220 return strtou64<T>(str, N, end, radix);
2221 }
2222
2234 template <class T>
2235 inline size_t strtoui(
2236 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2237 _Out_opt_ size_t* end,
2238 _In_ int radix)
2239 {
2240#if defined(_WIN64) || defined(__LP64__)
2241 return static_cast<size_t>(strtou64(str, count, end, radix));
2242#else
2243 return static_cast<size_t>(strtou32(str, count, end, radix));
2244#endif
2245 }
2246
2257 template <class T, size_t N>
2258 inline size_t strtoui(
2259 _In_ const T (&str)[N],
2260 _Out_opt_ size_t* end,
2261 _In_ int radix)
2262 {
2263 return strtoui<T>(str, N, end, radix);
2264 }
2265
2276 inline double strtod(
2277 _In_reads_or_z_opt_(count) const char* str, _In_ size_t count,
2278 _Out_opt_ size_t* end,
2279 _In_opt_ locale_t locale)
2280 {
2281 std::string tmp(str, strnlen(str, count));
2282 char* _end;
2283 double r;
2284#if _WIN32
2285 r = _strtod_l(tmp.c_str(), &_end, locale);
2286#else
2287 r = strtod_l(tmp.c_str(), &_end, locale);
2288#endif
2289 if (end) *end = (size_t)(_end - tmp.c_str());
2290 return r;
2291 }
2292
2303 inline double strtod(
2304 _In_reads_or_z_opt_(count) const wchar_t* str, _In_ size_t count,
2305 _Out_opt_ size_t* end,
2306 _In_opt_ locale_t locale)
2307 {
2308 std::wstring tmp(str, strnlen(str, count));
2309 wchar_t* _end;
2310 double r;
2311#if _WIN32
2312 r = _wcstod_l(tmp.c_str(), &_end, locale);
2313#else
2314 r = wcstod_l(tmp.c_str(), &_end, locale);
2315#endif
2316 if (end) *end = (size_t)(_end - tmp.c_str());
2317 return r;
2318 }
2319
2321 inline int vsnprintf(_Out_z_cap_(capacity) char* str, _In_ size_t capacity, _In_z_ _Printf_format_string_params_(2) const char* format, _In_opt_ locale_t locale, _In_ va_list arg)
2322 {
2323 int r;
2324#ifdef _WIN32
2325 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
2326#pragma warning(suppress: 4996)
2327 r = _vsnprintf_l(str, capacity, format, locale, arg);
2328#else
2329 r = ::vsnprintf(str, capacity, format, arg);
2330#endif
2331 if (r == -1 && strnlen(str, capacity) == capacity) {
2332 // Buffer overrun. Estimate buffer size for the next iteration.
2333 capacity += std::max<size_t>(capacity / 8, 0x80);
2334 if (capacity > INT_MAX)
2335 throw std::invalid_argument("string too big");
2336 return (int)capacity;
2337 }
2338 return r;
2339 }
2340
2341 inline int vsnprintf(_Out_z_cap_(capacity) wchar_t* str, _In_ size_t capacity, _In_z_ _Printf_format_string_params_(2) const wchar_t* format, _In_opt_ locale_t locale, _In_ va_list arg)
2342 {
2343 int r;
2344#ifdef _WIN32
2345 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
2346#pragma warning(suppress: 4996)
2347 r = _vsnwprintf_l(str, capacity, format, locale, arg);
2348#else
2349 r = vswprintf(str, capacity, format, arg);
2350#endif
2351 if (r == -1 && strnlen(str, capacity) == capacity) {
2352 // Buffer overrun. Estimate buffer size for the next iteration.
2353 capacity += std::max<size_t>(capacity / 8, 0x80);
2354 if (capacity > INT_MAX)
2355 throw std::invalid_argument("string too big");
2356 return (int)capacity;
2357 }
2358 return r;
2359 }
2361
2372 template<class T, class TR, class AX>
2373 inline size_t vappendf(_Inout_ std::basic_string<T, TR, AX>& str, _In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, _In_ va_list arg)
2374 {
2375 T buf[1024 / sizeof(T)];
2376
2377 // Try with stack buffer first.
2378 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
2379 if (count >= 0) {
2380 // Copy from stack.
2381 str.append(buf, count);
2382 return count;
2383 }
2384 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2385 // Allocate on heap and retry.
2386 auto buf_dyn = std::make_unique<T[]>(capacity);
2387 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
2388 if (count >= 0) {
2389 str.append(buf_dyn.get(), count);
2390 return count;
2391 }
2392 }
2393 }
2394
2404 template<class T, class TR, class AX>
2405 inline size_t appendf(_Inout_ std::basic_string<T, TR, AX>& str, _In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, ...)
2406 {
2407 va_list arg;
2408 va_start(arg, locale);
2409 size_t n = vappendf(str, format, locale, arg);
2410 va_end(arg);
2411 return n;
2412 }
2413
2422 template<class T, class TR, class AX>
2423 inline void vsprintf(_Inout_ std::basic_string<T, TR, AX>& str, _In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, _In_ va_list arg)
2424 {
2425 str.clear();
2426 vappendf(str, format, locale, arg);
2427 }
2428
2436 template<class T, class TR, class AX>
2437 inline void sprintf(_Inout_ std::basic_string<T, TR, AX>& str, _In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, ...)
2438 {
2439 va_list arg;
2440 va_start(arg, locale);
2441 vsprintf(str, format, locale, arg);
2442 va_end(arg);
2443 }
2444
2454 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2455 inline std::basic_string<T, TR, AX> vsprintf(_In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, _In_ va_list arg)
2456 {
2457 std::basic_string<T, TR, AX> str;
2458 vappendf(str, format, locale, arg);
2459 return str;
2460 }
2461
2470 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2471 inline std::basic_string<T, TR, AX> sprintf(_In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, ...)
2472 {
2473 va_list arg;
2474 va_start(arg, locale);
2475 auto str = vsprintf(format, locale, arg);
2476 va_end(arg);
2477 return str;
2478 }
2479
2481 inline size_t strftime(_Out_z_cap_(capacity) char* str, _In_ size_t capacity, _In_z_ _Printf_format_string_ const char* format, _In_ const struct tm* time, _In_opt_ locale_t locale)
2482 {
2483#ifdef _WIN32
2484 return _strftime_l(str, capacity, format, time, locale);
2485#else
2486 return strftime_l(str, capacity, format, time, locale);
2487#endif
2488 }
2489
2490 inline size_t strftime(_Out_z_cap_(capacity) wchar_t* str, _In_ size_t capacity, _In_z_ _Printf_format_string_ const wchar_t* format, _In_ const struct tm* time, _In_opt_ locale_t locale)
2491 {
2492#ifdef _WIN32
2493 return _wcsftime_l(str, capacity, format, time, locale);
2494#else
2495 return wcsftime_l(str, capacity, format, time, locale);
2496#endif
2497 }
2499
2508 template<class T, class TR, class AX>
2509 inline void strcatftime(_Inout_ std::basic_string<T, TR, AX>& str, _In_z_ _Printf_format_string_ const T* format, _In_ const struct tm* time, _In_opt_ locale_t locale)
2510 {
2511 T buf[1024 / sizeof(T)];
2512
2513 // Try with stack buffer first.
2514 size_t count = strftime(buf, _countof(buf), format, time, locale);
2515 if (count) {
2516 // Copy from stack.
2517 str.append(buf, count);
2518 }
2519 else {
2520 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2521 // Allocate on heap and retry.
2522 auto buf_dyn = std::make_unique<T[]>(capacity);
2523 count = strftime(buf_dyn.get(), capacity, format, time, locale);
2524 if (count) {
2525 str.append(buf_dyn.get(), count);
2526 break;
2527 }
2528 }
2529 }
2530 }
2531
2540 template<class T, class TR, class AX>
2541 inline void strftime(_Inout_ std::basic_string<T, TR, AX>& str, _In_z_ _Printf_format_string_ const T* format, _In_ const struct tm* time, _In_opt_ locale_t locale)
2542 {
2543 str.clear();
2544 strcatftime(str, format, time, locale);
2545 }
2546
2557 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2558 inline std::basic_string<T, TR, AX> strftime(_In_z_ _Printf_format_string_ const T* format, _In_ const struct tm* time, _In_opt_ locale_t locale)
2559 {
2560 std::basic_string<T, TR, AX> str;
2561 strcatftime(str, format, time, locale);
2562 return str;
2563 }
2564
2570 //template<class T>
2571 //inline void strlwr(_Inout_z_ T* str)
2572 //{
2573 // _Assume_(str);
2574 // for (size_t i = 0; str[i]; ++i)
2575 // str[i] = tolower(str[i]);
2576 //}
2577
2584 //template<class T>
2585 //inline void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
2586 //{
2587 // _Assume_(str);
2588 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2589 // for (size_t i = 0; str[i]; ++i)
2590 // str[i] = ctype.tolower(str[i]);
2591 //}
2592
2599 template<class T>
2600 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2601 {
2602 _Assume_(str || !count);
2603 for (size_t i = 0; i < count && str[i]; ++i)
2604 str[i] = tolower(str[i]);
2605 }
2606
2614 template<class T>
2615 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2616 {
2617 _Assume_(str || !count);
2618 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2619 for (size_t i = 0; i < count && str[i]; ++i)
2620 str[i] = ctype.tolower(str[i]);
2621 }
2622
2628 template<class T, size_t N>
2629 inline void strlwr(_Inout_ T (&str)[N])
2630 {
2631 strlwr(str, count);
2632 }
2633
2640 template<class T, size_t N>
2641 inline void strlwr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2642 {
2643 strlwr(str, count, locale);
2644 }
2645
2651 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2652 inline void strlwr(_Inout_ std::basic_string<T, TR, AX>& str)
2653 {
2654 for (auto& c : str)
2655 c = tolower(c);
2656 }
2657
2664 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2665 inline void strlwr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2666 {
2667 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2668 for (auto& c : str)
2669 c = ctype.tolower(c);
2670 }
2671
2677 //template<class T>
2678 //inline void strupr(_Inout_z_ T* str)
2679 //{
2680 // _Assume_(str);
2681 // for (size_t i = 0; str[i]; ++i)
2682 // str[i] = toupper(str[i]);
2683 //}
2684
2691 //template<class T>
2692 //inline void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
2693 //{
2694 // _Assume_(str);
2695 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2696 // for (size_t i = 0; str[i]; ++i)
2697 // str[i] = ctype.toupper(str[i]);
2698 //}
2699
2706 template<class T>
2707 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2708 {
2709 _Assume_(str || !count);
2710 for (size_t i = 0; i < count && str[i]; ++i)
2711 str[i] = toupper(str[i]);
2712 }
2713
2721 template<class T>
2722 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2723 {
2724 _Assume_(str || !count);
2725 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2726 for (size_t i = 0; i < count && str[i]; ++i)
2727 str[i] = ctype.toupper(str[i]);
2728 }
2729
2735 template<class T, size_t N>
2736 inline void strupr(_Inout_ T (&str)[N])
2737 {
2738 return strupr(str, N);
2739 }
2740
2747 template<class T, size_t N>
2748 inline void strupr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2749 {
2750 return strupr(str, N, locale);
2751 }
2752
2758 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2759 inline void strupr(_Inout_ std::basic_string<T, TR, AX>& str)
2760 {
2761 for (auto& c : str)
2762 c = toupper(c);
2763 }
2764
2771 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2772 inline void strupr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2773 {
2774 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2775 for (auto& c : str)
2776 c = ctype.toupper(c);
2777 }
2778
2787 template<class T>
2788 inline size_t ltrim(
2789 _Inout_z_count_(count) T* str, _In_ size_t count)
2790 {
2791 for (size_t i = 0;; ++i) {
2792 if (i >= count) {
2793 if (count) str[0] = 0;
2794 return 0;
2795 }
2796 if (!str[i]) {
2797 str[0] = 0;
2798 return 0;
2799 }
2800 if (!isspace(str[i])) {
2801 if (!i)
2802 return strnlen(str, count);
2803 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2804 str[n] = 0;
2805 return n;
2806 }
2807 }
2808 }
2809
2819 template<class T>
2820 inline size_t ltrim(
2821 _Inout_z_count_(count) T* str, _In_ size_t count,
2822 _In_ const std::locale& locale)
2823 {
2824 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2825 for (size_t i = 0;; ++i) {
2826 if (i >= count) {
2827 if (count) str[0] = 0;
2828 return 0;
2829 }
2830 if (!str[i]) {
2831 str[0] = 0;
2832 return 0;
2833 }
2834 if (!ctype.is(ctype.space, str[i])) {
2835 if (!i)
2836 return strnlen(str, count);
2837 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2838 str[n] = 0;
2839 return n;
2840 }
2841 }
2842 }
2843
2849 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2850 inline void ltrim(_Inout_ std::basic_string<T, TR, AX>& s)
2851 {
2852 s.erase(
2853 s.begin(),
2854 std::find_if(
2855 s.begin(),
2856 s.end(),
2857 [&](_In_ T ch) { return !isspace(ch); }));
2858 }
2859
2866 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2867 inline void ltrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
2868 {
2869 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2870 s.erase(
2871 s.begin(),
2872 std::find_if(
2873 s.begin(),
2874 s.end(),
2875 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }));
2876 }
2877
2886 template<class T>
2887 inline size_t rtrim(
2888 _Inout_z_count_(count) T* str, _In_ size_t count)
2889 {
2890 for (size_t i = 0, j = 0;;) {
2891 if (i >= count || !str[i]) {
2892 if (j < count) str[j] = 0;
2893 return j;
2894 }
2895 if (!isspace(str[i]))
2896 j = ++i;
2897 else
2898 ++i;
2899 }
2900 }
2901
2911 template<class T>
2912 inline size_t rtrim(
2913 _Inout_z_count_(count) T* str, _In_ size_t count,
2914 _In_ const std::locale& locale)
2915 {
2916 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2917 for (size_t i = 0, j = 0;;) {
2918 if (i >= count || !str[i]) {
2919 if (j < count) str[j] = 0;
2920 return j;
2921 }
2922 if (!ctype.is(ctype.space, str[i]))
2923 j = ++i;
2924 else
2925 ++i;
2926 }
2927 }
2928
2934 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2935 static inline void rtrim(_Inout_ std::basic_string<T, TR, AX>& s)
2936 {
2937 s.erase(
2938 std::find_if(
2939 s.rbegin(),
2940 s.rend(),
2941 [&](_In_ T ch) { return !isspace(ch); }).base(),
2942 s.end());
2943 }
2944
2951 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2952 static inline void rtrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
2953 {
2954 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2955 s.erase(
2956 std::find_if(
2957 s.rbegin(),
2958 s.rend(),
2959 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }).base(),
2960 s.end());
2961 }
2962
2971 template<class T>
2972 inline size_t trim(
2973 _Inout_z_count_(count) T* str, _In_ size_t count)
2974 {
2975 return ltrim(str, rtrim(str, count));
2976 }
2977
2987 template<class T>
2988 inline size_t trim(
2989 _Inout_z_count_(count) T* str, _In_ size_t count,
2990 _In_ const std::locale& locale)
2991 {
2992 return ltrim(str, rtrim(str, count, locale), locale);
2993 }
2994
3000 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3001 static inline void trim(_Inout_ std::basic_string<T, TR, AX>& s)
3002 {
3003 auto nonspace = [&](_In_ T ch) { return !isspace(ch); };
3004 s.erase(
3005 s.begin(),
3006 std::find_if(
3007 s.begin(),
3008 s.end(),
3009 nonspace));
3010 s.erase(
3011 std::find_if(
3012 s.rbegin(),
3013 s.rend(),
3014 nonspace).base(),
3015 s.end());
3016 }
3017
3024 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3025 static inline void trim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3026 {
3027 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3028 auto nonspace = [&](_In_ T ch) { return !ctype.is(ctype.space, ch); };
3029 s.erase(
3030 s.begin(),
3031 std::find_if(
3032 s.begin(),
3033 s.end(),
3034 nonspace));
3035 s.erase(
3036 std::find_if(
3037 s.rbegin(),
3038 s.rend(),
3039 nonspace).base(),
3040 s.end());
3041 }
3042}