stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
string.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2016-2024 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 using utf16_t = wchar_t;
31 using utf32_t = char32_t;
32#else
33 using utf16_t = char16_t;
34 using utf32_t = wchar_t;
35#endif
36
42 inline bool is_high_surrogate(_In_ utf16_t chr)
43 {
44 return 0xd800 < chr && chr < 0xdc00;
45 }
46
52 inline bool is_low_surrogate(_In_ utf16_t chr)
53 {
54 return 0xdc00 < chr && chr < 0xe000;
55 }
56
62 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
63 {
64 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
65 }
66
72 inline utf32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
73 {
74 _Assume_(is_surrogate_pair(str));
75 return
76 (static_cast<utf32_t>(str[0] - 0xd800) << 10) +
77 static_cast<utf32_t>(str[1] - 0xdc00) +
78 0x10000;
79 }
80
86 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ utf32_t chr)
87 {
88 _Assume_(chr >= 0x10000);
89 chr -= 0x10000;
90 str[0] = 0xd800 + static_cast<utf16_t>((chr >> 10) & 0x3ff);
91 str[1] = 0xdc00 + static_cast<utf16_t>(chr & 0x3ff);
92 }
93
99 inline bool iscombining(_In_ utf32_t chr)
100 {
101 return
102 (0x0300 <= chr && chr < 0x0370) ||
103 (0x1dc0 <= chr && chr < 0x1e00) ||
104 (0x20d0 <= chr && chr < 0x2100) ||
105 (0xfe20 <= chr && chr < 0xfe30);
106 }
107
113 template <class T>
114 bool islbreak(_In_ T chr)
115 {
116 return chr == '\n' || chr == '\r';
117 }
118
127 template <class T>
128 size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
129 {
130 _Assume_(chr || !count);
131 if (count >= 2 && ((chr[0] == '\r' && chr[1] == '\n') || (chr[0] == '\n' && chr[1] == '\r')))
132 return 2;
133 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
134 return 1;
135 return 0;
136 }
137
143 template <class T>
144 bool isspace(_In_ T chr)
145 {
146 return chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' || chr == '\v' || chr == '\f';
147 }
148
154 template <class T>
155 bool islower(_In_ T chr)
156 {
157 return 'a' <= chr && chr <= 'z';
158 }
159
165 template <class T>
166 bool isupper(_In_ T chr)
167 {
168 return 'A' <= chr && chr <= 'Z';
169 }
170
176 template <class T>
177 bool isdigit(_In_ T chr)
178 {
179 return '0' <= chr && chr <= '9';
180 }
181
187 template <class T>
188 bool isalpha(_In_ T chr)
189 {
190 return islower(chr) || isupper(chr);
191 }
192
198 template <class T>
199 bool is7bit(_In_ T chr)
200 {
201 return '\x00' <= chr && chr <= '\x7f';
202 }
203
210 inline size_t glyphlen(_In_reads_or_z_opt_(count) const utf16_t* glyph, _In_ size_t count)
211 {
212 _Assume_(glyph || !count);
213 if (count) {
214 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
215 for (; i < count && iscombining(glyph[i]); ++i);
216 return i;
217 }
218 return 0;
219 }
220
227 inline size_t glyphlen(_In_reads_or_z_opt_(count) const utf32_t* glyph, _In_ size_t count)
228 {
229 _Assume_(glyph || !count);
230 if (count) {
231 size_t i = 1;
232 for (; i < count && iscombining(glyph[i]); ++i);
233 return i;
234 }
235 return 0;
236 }
237
244 inline size_t glyphrlen(_In_reads_or_z_opt_(count) const utf16_t* str, _In_ size_t count)
245 {
246 _Assume_(count && str && str[count - 1]);
247 for (size_t i = count; i--;) {
248 if (!iscombining(str[i]))
249 return count - (!is_low_surrogate(str[i]) || i == 0 || !is_high_surrogate(str[i - 1]) ? i : i - 1);
250 }
251 return count;
252 }
253
260 inline size_t glyphrlen(_In_reads_or_z_opt_(count) const utf32_t* str, _In_ size_t count)
261 {
262 _Assume_(count && str && str[count - 1]);
263 for (size_t i = count; i--;) {
264 if (!iscombining(str[i]))
265 return count - (i == 0 ? i : i - 1);
266 }
267 return count;
268 }
269
277 template <class T>
278 T tolower(_In_ T chr)
279 {
280 return isupper(chr) ? chr | 0x20 : chr;
281 }
282
290 template <class T>
291 T toupper(_In_ T chr)
292 {
293 return islower(chr) ? chr | ~0x20 : chr;
294 }
295
303 template <class T>
304 size_t strlen(_In_z_ const T* str)
305 {
306 _Assume_(str);
307 size_t i;
308 for (i = 0; str[i]; ++i);
309 return i;
310 }
311
320 template <class T>
321 size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
322 {
323 _Assume_(str || !count);
324 size_t i;
325 for (i = 0; i < count && str[i]; ++i);
326 return i;
327 }
328
336 template <class T, size_t N>
337 size_t strnlen(_In_ const T (&str)[N])
338 {
339 return strnlen(str, N);
340 }
341
342 constexpr auto npos{ static_cast<size_t>(-1) };
343
352 template <class T>
353 size_t strchr(_In_z_ const T* str, _In_ T chr)
354 {
355 _Assume_(str);
356 for (size_t i = 0; str[i]; ++i)
357 if (str[i] == chr) return i;
358 return npos;
359 }
360
370 template <class T>
371 size_t strnchr(
372 _In_reads_or_z_opt_(count) const T* str,
373 _In_ size_t count,
374 _In_ T chr)
375 {
376 _Assume_(str || !count);
377 for (size_t i = 0; i < count && str[i]; ++i)
378 if (str[i] == chr) return i;
379 return npos;
380 }
381
390 template <class T, size_t N>
391 size_t strnchr(
392 _In_ const T (&str)[N],
393 _In_ T chr)
394 {
395 return strnchr(str, N, chr);
396 }
397
406 template <class T>
407 size_t strrchr(
408 _In_z_ const T* str,
409 _In_ T chr)
410 {
411 _Assume_(str);
412 size_t z = npos;
413 for (size_t i = 0; str[i]; ++i)
414 if (str[i] == chr) z = i;
415 return z;
416 }
417
427 template <class T>
428 size_t strrnchr(
429 _In_reads_or_z_opt_(count) const T* str,
430 _In_ size_t count,
431 _In_ T chr)
432 {
433 _Assume_(str || !count);
434 size_t z = npos;
435 for (size_t i = 0; i < count && str[i]; ++i)
436 if (str[i] == chr) z = i;
437 return z;
438 }
439
448 template <class T, size_t N>
449 size_t strrnchr(
450 _In_ const T (&str)[N],
451 _In_ T chr)
452 {
453 return strrnchr(str, N, chr);
454 }
455
464 template <class T>
465 size_t strichr(
466 _In_z_ const T* str,
467 _In_ T chr)
468 {
469 _Assume_(str);
470 chr = tolower(chr);
471 for (size_t i = 0; str[i]; ++i)
472 if (tolower(str[i]) == chr) return i;
473 return npos;
474 }
475
485 template <class T>
486 size_t strichr(
487 _In_z_ const T* str,
488 _In_ T chr,
489 _In_ const std::locale& locale)
490 {
491 _Assume_(str);
492 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
493 chr = ctype.tolower(chr);
494 for (size_t i = 0; str[i]; ++i)
495 if (ctype.tolower(str[i]) == chr) return i;
496 return npos;
497 }
498
508 template <class T>
509 size_t strnichr(
510 _In_reads_or_z_opt_(count) const T* str,
511 _In_ size_t count,
512 _In_ T chr)
513 {
514 _Assume_(str || !count);
515 chr = tolower(chr);
516 for (size_t i = 0; i < count && str[i]; ++i)
517 if (tolower(str[i]) == chr) return i;
518 return npos;
519 }
520
531 template <class T>
532 size_t strnichr(
533 _In_reads_or_z_opt_(count) const T* str,
534 _In_ size_t count,
535 _In_ T chr,
536 _In_ const std::locale& locale)
537 {
538 _Assume_(str || !count);
539 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
540 chr = ctype.tolower(chr);
541 for (size_t i = 0; i < count && str[i]; ++i)
542 if (ctype.tolower(str[i]) == chr) return i;
543 return npos;
544 }
545
554 template <class T, size_t N>
555 size_t strnichr(
556 _In_ const T (&str)[N],
557 _In_ T chr)
558 {
559 return strnichr(str, N, chr);
560 }
561
571 template <class T, size_t N>
572 size_t strnichr(
573 _In_ const T (&str)[N],
574 _In_ T chr,
575 _In_ const std::locale& locale)
576 {
577 return strnichr(str, N, chr, locale);
578 }
579
588 template <class T>
589 size_t strrichr(
590 _In_z_ const T* str,
591 _In_ T chr)
592 {
593 _Assume_(str);
594 chr = tolower(chr);
595 size_t z = npos;
596 for (size_t i = 0; str[i]; ++i)
597 if (tolower(str[i]) == chr) z = i;
598 return z;
599 }
600
610 template <class T>
611 size_t strrichr(
612 _In_reads_or_z_opt_(count) const T* str,
613 _In_ T chr,
614 _In_ const std::locale& locale)
615 {
616 _Assume_(str);
617 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
618 chr = ctype.tolower(chr);
619 size_t z = npos;
620 for (size_t i = 0; str[i]; ++i)
621 if (ctype.tolower(str[i]) == chr) z = i;
622 return z;
623 }
624
634 template <class T>
635 size_t strrnichr(
636 _In_reads_or_z_opt_(count) const T* str,
637 _In_ size_t count,
638 _In_ T chr)
639 {
640 _Assume_(str || !count);
641 chr = tolower(chr);
642 size_t z = npos;
643 for (size_t i = 0; i < count && str[i]; ++i)
644 if (tolower(str[i]) == chr) z = i;
645 return z;
646 }
647
658 template <class T>
659 size_t strrnichr(
660 _In_reads_or_z_opt_(count) const T* str,
661 _In_ size_t count,
662 _In_ T chr,
663 _In_ const std::locale& locale)
664 {
665 _Assume_(str || !count);
666 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
667 chr = ctype.tolower(chr);
668 size_t z = npos;
669 for (size_t i = 0; i < count && str[i]; ++i)
670 if (ctype.tolower(str[i]) == chr) z = i;
671 return z;
672 }
673
682 template <class T, size_t N>
683 size_t strrnichr(
684 _In_ const T (&str)[N],
685 _In_ T chr)
686 {
687 return strrnichr(str, N, chr);
688 }
689
699 template <class T, size_t N>
700 size_t strrnichr(
701 _In_ const T (&str)[N],
702 _In_ T chr,
703 _In_ const std::locale& locale)
704 {
705 return strrnichr(str, N, chr, locale);
706 }
707
715 //template <class T>
716 //bool isblank(_In_z_ const T* str)
717 //{
718 // _Assume_(str);
719 // for (size_t i = 0; str[i]; ++i)
720 // if (!isspace(str[i]))
721 // return false;
722 // return true;
723 //}
724
733 //template <class T>
734 //bool isblank(
735 // _In_z_ const T* str,
736 // _In_ const std::locale& locale)
737 //{
738 // _Assume_(str);
739 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
740 // for (size_t i = 0; str[i]; ++i)
741 // if (!ctype.is(ctype.space, str[i]))
742 // return false;
743 // return true;
744 //}
745
754 template <class T>
755 bool isblank(
756 _In_reads_or_z_opt_(count) const T* str,
757 _In_ size_t count)
758 {
759 _Assume_(str || !count);
760 for (size_t i = 0; i < count && str[i]; ++i)
761 if (!isspace(str[i]))
762 return false;
763 return true;
764 }
765
775 template <class T>
776 bool isblank(
777 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
778 _In_ const std::locale& locale)
779 {
780 _Assume_(str || !count);
781 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
782 for (size_t i = 0; i < count && str[i]; ++i)
783 if (!ctype.is(ctype.space, str[i]))
784 return false;
785 return true;
786 }
787
795 template <class T, size_t N>
796 bool isblank(_In_ const T (&str)[N])
797 {
798 return isblank(str, N);
799 }
800
809 template <class T, size_t N>
810 bool isblank(
811 _In_ const T (&str)[N],
812 _In_ const std::locale& locale)
813 {
814 return isblank(str, N, locale);
815 }
816
817 // ///
818 // /// Checks if string contains all-ASCII characters
819 // ///
820 // /// \param[in] str String
821 // ///
822 // /// \return `true` if all characters are ASCII or `false` when any non-ASCII character is found in string.
823 // ///
824 // template <class T>
825 // bool is7bit(_In_z_ const T* str)
826 // {
827 // _Assume_(str);
828 // for (size_t i = 0; str[i]; i++)
829 // if (!is7bit(str[i]))
830 // return false;
831 // return true;
832 // }
833
842 template <class T>
843 bool is7bit(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
844 {
845 _Assume_(str || !count);
846 for (size_t i = 0; i < count && str[i]; i++)
847 if (!is7bit(str[i]))
848 return false;
849 return true;
850 }
851
859 template <class T, size_t N>
860 bool is7bit(_In_ const T (&str)[N])
861 {
862 return is7bit(str, N);
863 }
864
873 template <class T1, class T2>
874 int strcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
875 {
876 _Assume_(str1);
877 _Assume_(str2);
878 size_t i; T1 a; T2 b;
879 for (i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
880 if (a > b) return +1;
881 if (a < b) return -1;
882 }
883 if (str1[i]) return +1;
884 if (str2[i]) return -1;
885 return 0;
886 }
887
897 template <class T1, class T2>
898 int strncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
899 {
900 _Assume_(str1 || !count);
901 _Assume_(str2 || !count);
902 size_t i; T1 a; T2 b;
903 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
904 if (a > b) return +1;
905 if (a < b) return -1;
906 }
907 if (i < count && str1[i]) return +1;
908 if (i < count && str2[i]) return -1;
909 return 0;
910 }
911
922 template <class T1, class T2>
923 int strncmp(
924 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
925 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
926 {
927 _Assume_(str1 || !count1);
928 _Assume_(str2 || !count2);
929 size_t i; T1 a; T2 b;
930 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
931 if (a > b) return +1;
932 if (a < b) return -1;
933 }
934 if (i < count1 && str1[i]) return +1;
935 if (i < count2 && str2[i]) return -1;
936 return 0;
937 }
938
947 template <class T1, size_t N1, class T2, size_t N2>
948 int strncmp(
949 _In_ const T1 (&str1)[N1],
950 _In_ const T2 (&str2)[N2])
951 {
952 return strncmp(str1, N1, str2, N2);
953 }
954
965 inline int strncmp(
966 _In_reads_or_z_opt_(count1) const utf32_t* str1, _In_ size_t count1,
967 _In_reads_or_z_opt_(count2) const utf16_t* str2, _In_ size_t count2)
968 {
969 _Assume_(str1 || !count1);
970 _Assume_(str2 || !count2);
971 size_t i, j, j_next; utf32_t a, b;
972 for (i = 0, j = 0; i < count1 && j < count2; ++i, j = j_next) {
973 a = str1[i];
974 if (!a)
975 break;
976 if (j + 1 >= count2 || !is_surrogate_pair(&str2[j])) {
977 b = static_cast<utf32_t>(str2[j]);
978 j_next = j + 1;
979 }
980 else {
981 b = surrogate_pair_to_ucs4(&str2[j]);
982 j_next = j + 2;
983 }
984 if (!b)
985 break;
986 if (a > b) return +1;
987 if (a < b) return -1;
988 }
989 if (i < count1 && str1[i]) return +1;
990 if (j < count2 && str2[j]) return -1;
991 return 0;
992 }
993
1002 template <size_t N1, size_t N2>
1003 int strncmp(
1004 _In_ const utf32_t (&str1)[N1],
1005 _In_ const utf16_t (&str2)[N2])
1006 {
1007 return strncmp(str1, N1, str2, N2);
1008 }
1009
1018 template <class T1, class T2>
1019 int strrcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
1020 {
1021 size_t
1022 i = strlen(str1),
1023 j = strlen(str2);
1024 _Assume_(str1 || !i);
1025 _Assume_(str2 || !j);
1026 size_t k; T1 a; T2 b;
1027 for (k = 1; i && j; k++) {
1028 i--; j--;
1029 if ((a = str1[i]) > (b = str2[j])) return +1;
1030 if (a < b) return -1;
1031 }
1032 if (i && !j) return +1;
1033 if (!i && j) return -1;
1034 return 0;
1035 }
1036
1046 template <class T1, class T2>
1047 int strrncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
1048 {
1049 size_t
1050 i = strnlen(str1, count),
1051 j = strnlen(str2, count);
1052 _Assume_(str1 || !i);
1053 _Assume_(str2 || !j);
1054 size_t k; T1 a; T2 b;
1055 for (k = 1; i && j; k++) {
1056 i--; j--;
1057 if ((a = str1[i]) > (b = str2[j])) return +1;
1058 if (a < b) return -1;
1059 }
1060 if (i && !j) return +1;
1061 if (!i && j) return -1;
1062 return 0;
1063 }
1064
1075 template <class T1, class T2>
1076 int strrncmp(
1077 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1078 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1079 {
1080 size_t
1081 i = strnlen(str1, count1),
1082 j = strnlen(str2, count2);
1083 _Assume_(str1 || !i);
1084 _Assume_(str2 || !j);
1085 size_t k; T1 a; T2 b;
1086 for (k = 1; i && j; k++) {
1087 i--; j--;
1088 if ((a = str1[i]) > (b = str2[j])) return +1;
1089 if (a < b) return -1;
1090 }
1091 if (i && !j) return +1;
1092 if (!i && j) return -1;
1093 return 0;
1094 }
1095
1104 template <class T1, size_t N1, class T2, size_t N2>
1105 int strrncmp(
1106 _In_ const T1 (&str1)[N1],
1107 _In_ const T2 (&str2)[N2])
1108 {
1109 return strrncmp(str1, N1, str2, N2);
1110 }
1111
1120 template <class T1, class T2>
1121 int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
1122 {
1123 _Assume_(str1);
1124 _Assume_(str2);
1125 size_t i; T1 a; T2 b;
1126 for (i = 0; (a = tolower(str1[i])) | (b = tolower(str2[i])); ++i) {
1127 if (a > b) return +1;
1128 if (a < b) return -1;
1129 }
1130 if (str1[i]) return +1;
1131 if (str2[i]) return -1;
1132 return 0;
1133 }
1134
1144 template <class T1, class T2>
1145 int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
1146 {
1147 _Assume_(str1);
1148 _Assume_(str2);
1149 size_t i; T1 a; T2 b;
1150 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1151 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1152 for (i = 0; (a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i])); ++i) {
1153 if (a > b) return +1;
1154 if (a < b) return -1;
1155 }
1156 if (str1[i]) return +1;
1157 if (str2[i]) return -1;
1158 return 0;
1159 }
1160
1170 template <class T1, class T2>
1171 int strnicmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
1172 {
1173 _Assume_(str1 || !count);
1174 _Assume_(str2 || !count);
1175 size_t i; T1 a; T2 b;
1176 for (i = 0; i < count && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); ++i) {
1177 if (a > b) return +1;
1178 if (a < b) return -1;
1179 }
1180 if (i < count && str1[i]) return +1;
1181 if (i < count && str2[i]) return -1;
1182 return 0;
1183 }
1184
1195 template <class T1, class T2>
1196 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)
1197 {
1198 _Assume_(str1 || !count);
1199 _Assume_(str2 || !count);
1200 size_t i; T1 a; T2 b;
1201 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1202 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1203 for (i = 0; i < count && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); ++i) {
1204 if (a > b) return +1;
1205 if (a < b) return -1;
1206 }
1207 if (i < count && str1[i]) return +1;
1208 if (i < count && str2[i]) return -1;
1209 return 0;
1210 }
1211
1222 template <class T1, class T2>
1223 int strnicmp(
1224 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1225 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1226 {
1227 _Assume_(str1 || !count1);
1228 _Assume_(str2 || !count2);
1229 size_t i; T1 a; T2 b;
1230 for (i = 0; i < count1 && i < count2 && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); ++i) {
1231 if (a > b) return +1;
1232 if (a < b) return -1;
1233 }
1234 if (i < count1 && str1[i]) return +1;
1235 if (i < count2 && str2[i]) return -1;
1236 return 0;
1237 }
1238
1250 template <class T1, class T2>
1251 int strnicmp(
1252 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1253 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
1254 _In_ const std::locale& locale)
1255 {
1256 _Assume_(str1 || !count1);
1257 _Assume_(str2 || !count2);
1258 size_t i; T1 a; T2 b;
1259 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1260 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1261 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); ++i) {
1262 if (a > b) return +1;
1263 if (a < b) return -1;
1264 }
1265 if (i < count1 && str1[i]) return +1;
1266 if (i < count2 && str2[i]) return -1;
1267 return 0;
1268 }
1269
1278 template <class T1, size_t N1, class T2, size_t N2>
1279 int strnicmp(
1280 _In_ const T1 (&str1)[N1],
1281 _In_ const T2 (&str2)[N2])
1282 {
1283 strnicmp(str1, N1, str2, N2);
1284 }
1285
1295 template <class T1, size_t N1, class T2, size_t N2>
1296 int strnicmp(
1297 _In_ const T1 (&str1)[N1],
1298 _In_ const T2 (&str2)[N2],
1299 _In_ const std::locale& locale)
1300 {
1301 strnicmp(str1, N1, str2, N2, locale);
1302 }
1303
1313 template <class T>
1314 int strcoll(
1315 _In_z_ const T* str1,
1316 _In_z_ const T* str2,
1317 _In_ const std::locale& locale)
1318 {
1319 _Assume_(str1);
1320 _Assume_(str2);
1321 auto& collate = std::use_facet<std::collate<T>>(locale);
1322 return collate.compare(str1, str1 + strlen(str1), str2, str2 + strlen(str2));
1323 }
1324
1336 template <class T>
1337 int strncoll(
1338 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
1339 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
1340 _In_ const std::locale& locale)
1341 {
1342 _Assume_(str1 || !count1);
1343 _Assume_(str2 || !count2);
1344 auto& collate = std::use_facet<std::collate<T>>(locale);
1345 return collate.compare(str1, str1 + count1, str2, str2 + count2);
1346 }
1347
1357 template <class T, size_t N1, size_t N2>
1358 int strncoll(
1359 _In_ const T (&str1)[N1],
1360 _In_ const T (&str2)[N2],
1361 _In_ const std::locale& locale)
1362 {
1363 return strncoll(str1, N1, str2, N2, locale);
1364 }
1365
1374 template <class T1, class T2>
1375 size_t strstr(
1376 _In_z_ const T1* str,
1377 _In_z_ const T2* sample)
1378 {
1379 _Assume_(str);
1380 _Assume_(sample);
1381 for (size_t offset = 0;; ++offset) {
1382 for (size_t i = offset, j = 0;; ++i, ++j) {
1383 if (!sample[j])
1384 return offset;
1385 if (!str[i])
1386 return npos;
1387 if (str[i] != sample[j])
1388 break;
1389 }
1390 }
1391 }
1392
1402 template <class T1, class T2>
1403 size_t strnstr(
1404 _In_reads_or_z_opt_(count) const T1* str, _In_ size_t count,
1405 _In_z_ const T2* sample)
1406 {
1407 _Assume_(str || !count);
1408 _Assume_(sample);
1409 for (size_t offset = 0;; ++offset) {
1410 for (size_t i = offset, j = 0;; ++i, ++j) {
1411 if (!sample[j])
1412 return offset;
1413 if (i >= count || !str[i])
1414 return npos;
1415 if (str[i] != sample[j])
1416 break;
1417 }
1418 }
1419 }
1420
1429 template <class T1, size_t N1, class T2>
1430 size_t strnstr(
1431 _In_ const T1 (&str)[N1],
1432 _In_z_ const T2* sample)
1433 {
1434 return strnstr(str, N1, sample);
1435 }
1436
1445 template <class T1, class T2>
1446 size_t stristr(
1447 _In_z_ const T1* str,
1448 _In_z_ const T2* sample)
1449 {
1450 _Assume_(str);
1451 _Assume_(sample);
1452 for (size_t offset = 0;; ++offset) {
1453 for (size_t i = offset, j = 0;; ++i, ++j) {
1454 if (!sample[j])
1455 return offset;
1456 if (!str[i])
1457 return npos;
1458 if (tolower(str[i]) != tolower(sample[j]))
1459 break;
1460 }
1461 }
1462 }
1463
1473 template <class T1, class T2>
1474 size_t stristr(
1475 _In_z_ const T1* str,
1476 _In_z_ const T2* sample,
1477 _In_ const std::locale& locale)
1478 {
1479 _Assume_(str);
1480 _Assume_(sample);
1481 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1482 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1483 for (size_t offset = 0;; ++offset) {
1484 for (size_t i = offset, j = 0;; ++i, ++j) {
1485 if (!sample[j])
1486 return offset;
1487 if (!str[i])
1488 return npos;
1489 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1490 break;
1491 }
1492 }
1493 }
1494
1504 template <class T1, class T2>
1505 size_t strnistr(
1506 _In_reads_or_z_opt_(count) const T1* str,
1507 _In_ size_t count,
1508 _In_z_ const T2* sample)
1509 {
1510 _Assume_(str || !count);
1511 _Assume_(sample);
1512 for (size_t offset = 0;; ++offset) {
1513 for (size_t i = offset, j = 0;; ++i, ++j) {
1514 if (!sample[j])
1515 return offset;
1516 if (i >= count || !str[i])
1517 return npos;
1518 if (tolower(str[i]) != tolower(sample[j]))
1519 break;
1520 }
1521 }
1522 }
1523
1534 template <class T1, class T2>
1535 size_t strnistr(
1536 _In_reads_or_z_opt_(count) const T1* str,
1537 _In_ size_t count,
1538 _In_z_ const T2* sample,
1539 _In_ const std::locale& locale)
1540 {
1541 _Assume_(str || !count);
1542 _Assume_(sample);
1543 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1544 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1545 for (size_t offset = 0;; ++offset) {
1546 for (size_t i = offset, j = 0;; ++i, ++j) {
1547 if (!sample[j])
1548 return offset;
1549 if (i >= count || !str[i])
1550 return npos;
1551 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1552 break;
1553 }
1554 }
1555 }
1556
1565 template <class T1, size_t N1, class T2>
1566 size_t strnistr(
1567 _In_ const T1 (&str)[N1],
1568 _In_z_ const T2* sample)
1569 {
1570 return strnistr(str, N1, sample);
1571 }
1572
1582 template <class T1, size_t N1, class T2>
1583 size_t strnistr(
1584 _In_ const T1 (&str)[N1],
1585 _In_z_ const T2* sample,
1586 _In_ const std::locale& locale)
1587 {
1588 return strnistr(str, N1, sample, locale);
1589 }
1590
1599 template <class T1, class T2>
1600 size_t strcpy(
1601 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
1602 _In_z_ const T2* src)
1603 {
1604 _Assume_(dst);
1605 _Assume_(src);
1606 for (size_t i = 0; ; ++i) {
1607 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1608 return i;
1609 }
1610 }
1611
1621 template <class T1, class T2>
1622 size_t strncpy(
1623 _Out_writes_(count) _Post_maybez_ T1* dst,
1624 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1625 {
1626 _Assume_(dst || !count);
1627 _Assume_(src || !count);
1628 for (size_t i = 0; ; ++i) {
1629 if (i >= count)
1630 return i;
1631 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1632 return i;
1633 }
1634 }
1635
1646 template <class T1, class T2>
1647 size_t strncpy(
1648 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1649 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1650 {
1651 _Assume_(dst || !count_dst);
1652 _Assume_(src || !count_src);
1653 for (size_t i = 0; ; ++i)
1654 {
1655 if (i >= count_dst)
1656 return i;
1657 if (i >= count_src) {
1658 dst[i] = 0;
1659 return i;
1660 }
1661 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1662 return i;
1663 }
1664 }
1665
1674 template <class T1, size_t N1, class T2, size_t N2>
1675 size_t strncpy(
1676 _Out_ _Post_maybez_ T1 (&dst)[N1],
1677 _In_ const T2 (&src)[N2])
1678 {
1679 return strncpy(dst, N1, src, N2);
1680 }
1681
1690 template <class T1, class T2>
1691 size_t strcat(
1692 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
1693 _In_z_ const T2* src)
1694 {
1695 _Assume_(dst);
1696 _Assume_(src);
1697 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1698 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1699 return j;
1700 }
1701 }
1702
1712 template <class T1, class T2>
1713 size_t strncat(
1714 _Inout_z_ T1* dst,
1715 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1716 {
1717 _Assume_(dst || !count);
1718 _Assume_(src || !count);
1719 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1720 if (i >= count)
1721 return j;
1722 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1723 return j;
1724 }
1725 }
1726
1737 template <class T1, class T2>
1738 size_t strncat(
1739 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1740 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1741 {
1742 _Assume_(dst || !count_dst);
1743 _Assume_(src || !count_src);
1744 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
1745 {
1746 if (j >= count_dst)
1747 return j;
1748 if (i >= count_src) {
1749 dst[j] = 0;
1750 return j;
1751 }
1752 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1753 return j;
1754 }
1755 }
1756
1767 template <class T>
1768 _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
1769 {
1770 if (!str) _Unlikely_
1771 return nullptr;
1772 size_t count = strlen(str) + 1;
1773 T* dst = new T[count];
1774 strncpy(dst, count, str, SIZE_MAX);
1775 return dst;
1776 }
1777
1789 template <class T>
1790 _Ret_z_ T* strndup(
1791 _In_reads_or_z_opt_(count) const T* str,
1792 _In_ size_t count)
1793 {
1794 T* dst = new T[count];
1795 strncpy(dst, count, str, SIZE_MAX);
1796 return dst;
1797 }
1798
1809 template <class T, size_t N>
1810 _Check_return_ _Ret_maybenull_z_ T* strndup(_In_ const T (&str)[N])
1811 {
1812 return strndup(str, N);
1813 }
1814
1824 template <class T>
1825 size_t crlf2nl(_Out_writes_z_(_String_length_(src) + 1) T* dst, _In_z_ const T* src)
1826 {
1827 _Assume_(dst);
1828 _Assume_(src);
1829 size_t i, j;
1830 for (i = j = 0; src[j];) {
1831 if (src[j] != '\r' || src[j + 1] != '\n')
1832 dst[i++] = src[j++];
1833 else {
1834 dst[i++] = '\n';
1835 j += 2;
1836 }
1837 }
1838 dst[i] = 0;
1839 return i;
1840 }
1841
1848 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1849 void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& dst, _In_z_ const T* src)
1850 {
1851 _Assume_(src);
1852 _Assume_(src != dst.data());
1853 dst.clear();
1854 dst.reserve(strlen(src));
1855 for (size_t j = 0; src[j];) {
1856 if (src[j] != '\r' || src[j + 1] != '\n')
1857 dst += src[j++];
1858 else {
1859 dst += '\n';
1860 j += 2;
1861 }
1862 }
1863 }
1864
1870 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1871 void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& str)
1872 {
1873 size_t i, j, n;
1874 for (i = j = 0, n = str.size(); j < n;) {
1875 if (str[j] != '\r' || str[j + 1] != '\n')
1876 str[i++] = str[j++];
1877 else {
1878 str[i++] = '\n';
1879 j += 2;
1880 }
1881 }
1882 str.resize(i);
1883 }
1884
1886 template <class T, class T_bin>
1887 T_bin strtoint(
1888 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1889 _Out_opt_ size_t* end,
1890 _In_ int radix,
1891 _Out_ uint8_t& flags)
1892 {
1893 _Assume_(str || !count);
1894 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
1895
1896 size_t i = 0;
1897 T_bin value = 0, digit,
1898 max_ui = (T_bin)-1,
1899 max_ui_pre1, max_ui_pre2;
1900
1901 flags = 0;
1902
1903 // Skip leading spaces.
1904 for (;; ++i) {
1905 if (i >= count || !str[i]) goto error;
1906 if (!isspace(str[i])) break;
1907 }
1908
1909 // Read the sign.
1910 if (str[i] == '+') {
1911 flags &= ~0x01;
1912 ++i;
1913 if (i >= count || !str[i]) goto error;
1914 }
1915 else if (str[i] == '-') {
1916 flags |= 0x01;
1917 ++i;
1918 if (i >= count || !str[i]) goto error;
1919 }
1920
1921 if (radix == 16) {
1922 // On hexadecimal, allow leading 0x.
1923 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
1924 i += 2;
1925 if (i >= count || !str[i]) goto error;
1926 }
1927 }
1928 else if (!radix) {
1929 // Autodetect radix.
1930 if (str[i] == '0') {
1931 ++i;
1932 if (i >= count || !str[i]) goto error;
1933 if (str[i] == 'x' || str[i] == 'X') {
1934 radix = 16;
1935 ++i;
1936 if (i >= count || !str[i]) goto error;
1937 }
1938 else
1939 radix = 8;
1940 }
1941 else
1942 radix = 10;
1943 }
1944
1945 // We have the radix.
1946 max_ui_pre1 = max_ui / (T_bin)radix;
1947 max_ui_pre2 = max_ui % (T_bin)radix;
1948 for (;;) {
1949 if ('0' <= str[i] && str[i] <= '9')
1950 digit = (T_bin)str[i] - '0';
1951 else if ('A' <= str[i] && str[i] <= 'Z')
1952 digit = (T_bin)str[i] - 'A' + '\x0a';
1953 else if ('a' <= str[i] && str[i] <= 'z')
1954 digit = (T_bin)str[i] - 'a' + '\x0a';
1955 else
1956 goto error;
1957 if (digit >= (T_bin)radix)
1958 goto error;
1959
1960 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
1961 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
1962 value = value * (T_bin)radix + digit;
1963 else {
1964 // Overflow!
1965 flags |= 0x02;
1966 }
1967
1968 ++i;
1969 if (i >= count || !str[i])
1970 goto error;
1971 }
1972
1973 error:
1974 if (end) *end = i;
1975 return value;
1976 }
1978
1989 template <class T, class T_bin>
1990 T_bin strtoint(
1991 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1992 _Out_opt_ size_t* end,
1993 _In_ int radix)
1994 {
1995 uint8_t flags;
1996 T_bin value;
1997
1998 switch (sizeof(T_bin)) {
1999 case 1:
2000 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
2001 if ((flags & 0x01) && (value & 0x80)) {
2002 // Sign bit is 1 => overflow.
2003 flags |= 0x02;
2004 }
2005 return (flags & 0x02) ?
2006 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
2007 (flags & 0x01) ? -value : value;
2008
2009 case 2:
2010 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
2011 if ((flags & 0x01) && (value & 0x8000)) {
2012 // Sign bit is 1 => overflow.
2013 flags |= 0x02;
2014 }
2015 return (flags & 0x02) ?
2016 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
2017 (flags & 0x01) ? -value : value;
2018
2019 case 4:
2020 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
2021 if ((flags & 0x01) && (value & 0x80000000)) {
2022 // Sign bit is 1 => overflow.
2023 flags |= 0x02;
2024 }
2025 return (flags & 0x02) ?
2026 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
2027 (flags & 0x01) ? -value : value;
2028
2029 case 8:
2030 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
2031 if ((flags & 0x01) && (value & 0x8000000000000000)) {
2032 // Sign bit is 1 => overflow.
2033 flags |= 0x02;
2034 }
2035 return (flags & 0x02) ?
2036 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
2037 (flags & 0x01) ? -value : value;
2038
2039 default:
2040 throw std::invalid_argument("Unsupported bit length");
2041 }
2042 }
2043
2053 template <class T, size_t N, class T_bin>
2054 T_bin strtoint(
2055 _In_ const T (&str)[N],
2056 _Out_opt_ size_t* end,
2057 _In_ int radix)
2058 {
2059 return strtoint<T, T_bin>(str, N, end, radix);
2060 }
2061
2072 template <class T, class T_bin>
2073 T_bin strtouint(
2074 _In_reads_or_z_opt_(count) const T* str,
2075 _In_ size_t count,
2076 _Out_opt_ size_t* end,
2077 _In_ int radix)
2078 {
2079 uint8_t flags;
2080 T_bin value;
2081
2082 switch (sizeof(T_bin)) {
2083 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
2084 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
2085 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
2086 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
2087 default: throw std::invalid_argument("Unsupported bit length");
2088 }
2089
2090 return (flags & 0x02) ?
2091 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
2092 (flags & 0x01) ? ~value : value;
2093 }
2094
2104 template <class T, size_t N, class T_bin>
2105 T_bin strtouint(
2106 _In_ const T (&str)[N],
2107 _Out_opt_ size_t* end,
2108 _In_ int radix)
2109 {
2110 return strtouint<T, T_bin>(str, N, end, radix);
2111 }
2112
2123 template <class T>
2124 int32_t strto32(
2125 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2126 _Out_opt_ size_t* end,
2127 _In_ int radix)
2128 {
2129 return strtoint<T, int32_t>(str, count, end, radix);
2130 }
2131
2141 template <class T, size_t N>
2142 int32_t strto32(
2143 _In_ const T (&str)[N],
2144 _Out_opt_ size_t* end,
2145 _In_ int radix)
2146 {
2147 return strto32<T>(str, N, end, radix);
2148 }
2149
2160 template <class T>
2161 int64_t strto64(
2162 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2163 _Out_opt_ size_t* end,
2164 _In_ int radix)
2165 {
2166 return strtoint<T, int64_t>(str, count, end, radix);
2167 }
2168
2178 template <class T, size_t N>
2179 int64_t strto64(
2180 _In_ const T (&str)[N],
2181 _Out_opt_ size_t* end,
2182 _In_ int radix)
2183 {
2184 return strto64<T>(str, N, end, radix);
2185 }
2186
2198 template <class T>
2199 ptrdiff_t strtoi(
2200 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2201 _Out_opt_ size_t* end,
2202 _In_ int radix)
2203 {
2204#if defined(_WIN64) || defined(__LP64__)
2205 return static_cast<ptrdiff_t>(strto64(str, count, end, radix));
2206#else
2207 return static_cast<ptrdiff_t>(strto32(str, count, end, radix));
2208#endif
2209 }
2210
2221 template <class T, size_t N>
2222 ptrdiff_t strtoi(
2223 _In_ const T (&str)[N],
2224 _Out_opt_ size_t* end,
2225 _In_ int radix)
2226 {
2227 return strtoi<T>(str, N, end, radix);
2228 }
2229
2240 template <class T>
2241 uint32_t strtou32(
2242 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2243 _Out_opt_ size_t* end,
2244 _In_ int radix)
2245 {
2246 return strtouint<T, uint32_t>(str, count, end, radix);
2247 }
2248
2258 template <class T, size_t N>
2259 uint32_t strtou32(
2260 _In_ const T (&str)[N],
2261 _Out_opt_ size_t* end,
2262 _In_ int radix)
2263 {
2264 return strtou32(str, N, end, radix);
2265 }
2266
2277 template <class T>
2278 uint64_t strtou64(
2279 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2280 _Out_opt_ size_t* end,
2281 _In_ int radix)
2282 {
2283 return strtouint<T, uint64_t>(str, count, end, radix);
2284 }
2285
2295 template <class T, size_t N>
2296 uint64_t strtou64(
2297 _In_ const T (&str)[N],
2298 _Out_opt_ size_t* end,
2299 _In_ int radix)
2300 {
2301 return strtou64<T>(str, N, end, radix);
2302 }
2303
2315 template <class T>
2316 size_t strtoui(
2317 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2318 _Out_opt_ size_t* end,
2319 _In_ int radix)
2320 {
2321#if defined(_WIN64) || defined(__LP64__)
2322 return static_cast<size_t>(strtou64(str, count, end, radix));
2323#else
2324 return static_cast<size_t>(strtou32(str, count, end, radix));
2325#endif
2326 }
2327
2338 template <class T, size_t N>
2339 size_t strtoui(
2340 _In_ const T (&str)[N],
2341 _Out_opt_ size_t* end,
2342 _In_ int radix)
2343 {
2344 return strtoui<T>(str, N, end, radix);
2345 }
2346
2357 inline double strtod(
2358 _In_reads_or_z_opt_(count) const char* str, _In_ size_t count,
2359 _Out_opt_ size_t* end,
2360 _In_opt_ locale_t locale)
2361 {
2362 count = strnlen(str, count);
2363 _Assume_(str || !count);
2364 std::string tmp(str, count);
2365 char* _end;
2366 double r;
2367#if _WIN32
2368 r = _strtod_l(tmp.c_str(), &_end, locale);
2369#else
2370 r = strtod_l(tmp.c_str(), &_end, locale);
2371#endif
2372 if (end) *end = (size_t)(_end - tmp.c_str());
2373 return r;
2374 }
2375
2386 inline double strtod(
2387 _In_reads_or_z_opt_(count) const wchar_t* str, _In_ size_t count,
2388 _Out_opt_ size_t* end,
2389 _In_opt_ locale_t locale)
2390 {
2391 count = strnlen(str, count);
2392 _Assume_(str || !count);
2393 std::wstring tmp(str, count);
2394 wchar_t* _end;
2395 double r;
2396#if _WIN32
2397 r = _wcstod_l(tmp.c_str(), &_end, locale);
2398#else
2399 r = wcstod_l(tmp.c_str(), &_end, locale);
2400#endif
2401 if (end) *end = (size_t)(_end - tmp.c_str());
2402 return r;
2403 }
2404
2406 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)
2407 {
2408#ifdef _WIN32
2409#pragma warning(suppress: 4996)
2410 return _vsnprintf_l(str, capacity, format, locale, arg);
2411#else
2412 va_list arg_mutable;
2413 va_copy(arg_mutable, arg);
2414 return ::vsnprintf_l(str, capacity, locale, format, arg_mutable);
2415#endif
2416 }
2417
2418 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)
2419 {
2420#ifdef _WIN32
2421#pragma warning(suppress: 4996)
2422 return _vsnwprintf_l(str, capacity, format, locale, arg);
2423#else
2424 va_list arg_mutable;
2425 va_copy(arg_mutable, arg);
2426 return ::vswprintf_l(str, capacity, locale, format, arg_mutable);
2427#endif
2428 }
2430
2441 template<class T, class TR, class AX>
2442 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)
2443 {
2444 T buf[1024 / sizeof(T)];
2445
2446 // Try with stack buffer first.
2447 int count = vsnprintf(buf, _countof(buf), format, locale, arg);
2448 if (0 <= count && static_cast<size_t>(count) <= _countof(buf)) {
2449 // Copy from stack.
2450 str.append(buf, static_cast<size_t>(count));
2451 return static_cast<size_t>(count);
2452 }
2453#ifdef _WIN32
2454 if (count < 0) {
2455 switch (errno) {
2456 case 0:
2457 count = vsnprintf(NULL, 0, format, locale, arg);
2458 _Assume_(count >= 0);
2459 break;
2460 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2461 case EILSEQ: throw std::runtime_error("encoding error");
2462 default: throw std::runtime_error("failed to format string");
2463 }
2464 }
2465 size_t offset = str.size();
2466 str.resize(offset + count);
2467 if (vsnprintf(&str[offset], count + 1, format, locale, arg) != count) _Unlikely_
2468 throw std::runtime_error("failed to format string");
2469#else
2470 size_t offset = str.size();
2471 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2472 switch (errno) {
2473 case EOVERFLOW:
2474 // Allocate on heap and retry.
2475 str.resize(offset + capacity);
2476 count = vsnprintf(&str[offset], capacity, format, locale, arg);
2477 if (0 <= count && static_cast<size_t>(count) <= capacity) {
2478 str.resize(offset + static_cast<size_t>(count));
2479 return static_cast<size_t>(count);
2480 }
2481 break;
2482 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2483 case EILSEQ: throw std::runtime_error("encoding error");
2484 default: throw std::runtime_error("failed to format string");
2485 }
2486 }
2487#endif
2488 return static_cast<size_t>(count);
2489 }
2490
2500 template<class T, class TR, class AX>
2501 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, ...)
2502 {
2503 va_list arg;
2504 va_start(arg, locale);
2505 size_t n = vappendf(str, format, locale, arg);
2506 va_end(arg);
2507 return n;
2508 }
2509
2518 template<class T, class TR, class AX>
2519 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)
2520 {
2521 str.clear();
2522 vappendf(str, format, locale, arg);
2523 }
2524
2532 template<class T, class TR, class AX>
2533 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, ...)
2534 {
2535 va_list arg;
2536 va_start(arg, locale);
2537 vsprintf(str, format, locale, arg);
2538 va_end(arg);
2539 }
2540
2550 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2551 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)
2552 {
2553 std::basic_string<T, TR, AX> str;
2554 vappendf(str, format, locale, arg);
2555 return str;
2556 }
2557
2566 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2567 std::basic_string<T, TR, AX> sprintf(_In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, ...)
2568 {
2569 va_list arg;
2570 va_start(arg, locale);
2571 auto str = vsprintf(format, locale, arg);
2572 va_end(arg);
2573 return str;
2574 }
2575
2577 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)
2578 {
2579#ifdef _WIN32
2580 return _strftime_l(str, capacity, format, time, locale);
2581#else
2582 return strftime_l(str, capacity, format, time, locale);
2583#endif
2584 }
2585
2586 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)
2587 {
2588#ifdef _WIN32
2589 return _wcsftime_l(str, capacity, format, time, locale);
2590#else
2591 return wcsftime_l(str, capacity, format, time, locale);
2592#endif
2593 }
2595
2604 template<class T, class TR, class AX>
2605 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)
2606 {
2607 T buf[1024 / sizeof(T)];
2608
2609 // Try with stack buffer first.
2610 size_t count = strftime(buf, _countof(buf), format, time, locale);
2611 if (count) {
2612 // Copy from stack.
2613 str.append(buf, count);
2614 return;
2615 }
2616 size_t offset = str.size();
2617 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2618 // Allocate on heap and retry.
2619 str.resize(offset + capacity);
2620 count = strftime(&str[offset], capacity + 1, format, time, locale);
2621 if (count) {
2622 str.resize(offset + count);
2623 return;
2624 }
2625 }
2626 }
2627
2636 template<class T, class TR, class AX>
2637 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)
2638 {
2639 str.clear();
2640 strcatftime(str, format, time, locale);
2641 }
2642
2652 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2653 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)
2654 {
2655 std::basic_string<T, TR, AX> str;
2656 strcatftime(str, format, time, locale);
2657 return str;
2658 }
2659
2665 //template<class T>
2666 //void strlwr(_Inout_z_ T* str)
2667 //{
2668 // _Assume_(str);
2669 // for (size_t i = 0; str[i]; ++i)
2670 // str[i] = tolower(str[i]);
2671 //}
2672
2679 //template<class T>
2680 //void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
2681 //{
2682 // _Assume_(str);
2683 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2684 // for (size_t i = 0; str[i]; ++i)
2685 // str[i] = ctype.tolower(str[i]);
2686 //}
2687
2694 template<class T>
2695 void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2696 {
2697 _Assume_(str || !count);
2698 for (size_t i = 0; i < count && str[i]; ++i)
2699 str[i] = tolower(str[i]);
2700 }
2701
2709 template<class T>
2710 void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2711 {
2712 _Assume_(str || !count);
2713 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2714 for (size_t i = 0; i < count && str[i]; ++i)
2715 str[i] = ctype.tolower(str[i]);
2716 }
2717
2723 template<class T, size_t N>
2724 void strlwr(_Inout_ T (&str)[N])
2725 {
2726 strlwr(str, N);
2727 }
2728
2735 template<class T, size_t N>
2736 void strlwr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2737 {
2738 strlwr(str, N, locale);
2739 }
2740
2746 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2747 void strlwr(_Inout_ std::basic_string<T, TR, AX>& str)
2748 {
2749 for (auto& c : str)
2750 c = tolower(c);
2751 }
2752
2759 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2760 void strlwr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2761 {
2762 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2763 for (auto& c : str)
2764 c = ctype.tolower(c);
2765 }
2766
2772 //template<class T>
2773 //void strupr(_Inout_z_ T* str)
2774 //{
2775 // _Assume_(str);
2776 // for (size_t i = 0; str[i]; ++i)
2777 // str[i] = toupper(str[i]);
2778 //}
2779
2786 //template<class T>
2787 //void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
2788 //{
2789 // _Assume_(str);
2790 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2791 // for (size_t i = 0; str[i]; ++i)
2792 // str[i] = ctype.toupper(str[i]);
2793 //}
2794
2801 template<class T>
2802 void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2803 {
2804 _Assume_(str || !count);
2805 for (size_t i = 0; i < count && str[i]; ++i)
2806 str[i] = toupper(str[i]);
2807 }
2808
2816 template<class T>
2817 void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2818 {
2819 _Assume_(str || !count);
2820 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2821 for (size_t i = 0; i < count && str[i]; ++i)
2822 str[i] = ctype.toupper(str[i]);
2823 }
2824
2830 template<class T, size_t N>
2831 void strupr(_Inout_ T (&str)[N])
2832 {
2833 return strupr(str, N);
2834 }
2835
2842 template<class T, size_t N>
2843 void strupr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2844 {
2845 return strupr(str, N, locale);
2846 }
2847
2853 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2854 void strupr(_Inout_ std::basic_string<T, TR, AX>& str)
2855 {
2856 for (auto& c : str)
2857 c = toupper(c);
2858 }
2859
2866 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2867 void strupr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2868 {
2869 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2870 for (auto& c : str)
2871 c = ctype.toupper(c);
2872 }
2873
2882 template<class T>
2883 size_t ltrim(
2884 _Inout_z_count_(count) T* str, _In_ size_t count)
2885 {
2886 for (size_t i = 0;; ++i) {
2887 if (i >= count) {
2888 if (count) str[0] = 0;
2889 return 0;
2890 }
2891 if (!str[i]) {
2892 str[0] = 0;
2893 return 0;
2894 }
2895 if (!isspace(str[i])) {
2896 if (!i)
2897 return strnlen(str, count);
2898 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2899 str[n] = 0;
2900 return n;
2901 }
2902 }
2903 }
2904
2914 template<class T>
2915 size_t ltrim(
2916 _Inout_z_count_(count) T* str, _In_ size_t count,
2917 _In_ const std::locale& locale)
2918 {
2919 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2920 for (size_t i = 0;; ++i) {
2921 if (i >= count) {
2922 if (count) str[0] = 0;
2923 return 0;
2924 }
2925 if (!str[i]) {
2926 str[0] = 0;
2927 return 0;
2928 }
2929 if (!ctype.is(ctype.space, str[i])) {
2930 if (!i)
2931 return strnlen(str, count);
2932 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2933 str[n] = 0;
2934 return n;
2935 }
2936 }
2937 }
2938
2944 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2945 void ltrim(_Inout_ std::basic_string<T, TR, AX>& s)
2946 {
2947 s.erase(
2948 s.begin(),
2949 std::find_if(
2950 s.begin(),
2951 s.end(),
2952 [&](_In_ T ch) { return !isspace(ch); }));
2953 }
2954
2961 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2962 void ltrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
2963 {
2964 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2965 s.erase(
2966 s.begin(),
2967 std::find_if(
2968 s.begin(),
2969 s.end(),
2970 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }));
2971 }
2972
2981 template<class T>
2982 size_t rtrim(
2983 _Inout_z_count_(count) T* str, _In_ size_t count)
2984 {
2985 for (size_t i = 0, j = 0;;) {
2986 if (i >= count || !str[i]) {
2987 if (j < count) str[j] = 0;
2988 return j;
2989 }
2990 if (!isspace(str[i]))
2991 j = ++i;
2992 else
2993 ++i;
2994 }
2995 }
2996
3006 template<class T>
3007 size_t rtrim(
3008 _Inout_z_count_(count) T* str, _In_ size_t count,
3009 _In_ const std::locale& locale)
3010 {
3011 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3012 for (size_t i = 0, j = 0;;) {
3013 if (i >= count || !str[i]) {
3014 if (j < count) str[j] = 0;
3015 return j;
3016 }
3017 if (!ctype.is(ctype.space, str[i]))
3018 j = ++i;
3019 else
3020 ++i;
3021 }
3022 }
3023
3029 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3030 void rtrim(_Inout_ std::basic_string<T, TR, AX>& s)
3031 {
3032 s.erase(
3033 std::find_if(
3034 s.rbegin(),
3035 s.rend(),
3036 [&](_In_ T ch) { return !isspace(ch); }).base(),
3037 s.end());
3038 }
3039
3046 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3047 void rtrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3048 {
3049 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3050 s.erase(
3051 std::find_if(
3052 s.rbegin(),
3053 s.rend(),
3054 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }).base(),
3055 s.end());
3056 }
3057
3066 template<class T>
3067 size_t trim(
3068 _Inout_z_count_(count) T* str, _In_ size_t count)
3069 {
3070 return ltrim(str, rtrim(str, count));
3071 }
3072
3082 template<class T>
3083 size_t trim(
3084 _Inout_z_count_(count) T* str, _In_ size_t count,
3085 _In_ const std::locale& locale)
3086 {
3087 return ltrim(str, rtrim(str, count, locale), locale);
3088 }
3089
3095 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3096 void trim(_Inout_ std::basic_string<T, TR, AX>& s)
3097 {
3098 auto nonspace = [&](_In_ T ch) { return !isspace(ch); };
3099 s.erase(
3100 s.begin(),
3101 std::find_if(
3102 s.begin(),
3103 s.end(),
3104 nonspace));
3105 s.erase(
3106 std::find_if(
3107 s.rbegin(),
3108 s.rend(),
3109 nonspace).base(),
3110 s.end());
3111 }
3112
3119 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3120 void trim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3121 {
3122 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3123 auto nonspace = [&](_In_ T ch) { return !ctype.is(ctype.space, ch); };
3124 s.erase(
3125 s.begin(),
3126 std::find_if(
3127 s.begin(),
3128 s.end(),
3129 nonspace));
3130 s.erase(
3131 std::find_if(
3132 s.rbegin(),
3133 s.rend(),
3134 nonspace).base(),
3135 s.end());
3136 }
3137}