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;
930 for (i = 0; i < count1 && i < count2; ++i) {
931 auto a = str1[i];
932 auto b = str2[i];
933 if (!a && !b) return 0;
934 if (a > b) return +1;
935 if (a < b) return -1;
936 }
937 if (i < count1 && str1[i]) return +1;
938 if (i < count2 && str2[i]) return -1;
939 return 0;
940 }
941
950 template <class T1, size_t N1, class T2, size_t N2>
951 int strncmp(
952 _In_ const T1 (&str1)[N1],
953 _In_ const T2 (&str2)[N2])
954 {
955 return strncmp(str1, N1, str2, N2);
956 }
957
968 inline int strncmp(
969 _In_reads_or_z_opt_(count1) const utf32_t* str1, _In_ size_t count1,
970 _In_reads_or_z_opt_(count2) const utf16_t* str2, _In_ size_t count2)
971 {
972 _Assume_(str1 || !count1);
973 _Assume_(str2 || !count2);
974 size_t i, j, j_next; utf32_t a, b;
975 for (i = 0, j = 0; i < count1 && j < count2; ++i, j = j_next) {
976 a = str1[i];
977 if (!a)
978 break;
979 if (j + 1 >= count2 || !is_surrogate_pair(&str2[j])) {
980 b = static_cast<utf32_t>(str2[j]);
981 j_next = j + 1;
982 }
983 else {
984 b = surrogate_pair_to_ucs4(&str2[j]);
985 j_next = j + 2;
986 }
987 if (!b)
988 break;
989 if (a > b) return +1;
990 if (a < b) return -1;
991 }
992 if (i < count1 && str1[i]) return +1;
993 if (j < count2 && str2[j]) return -1;
994 return 0;
995 }
996
1005 template <size_t N1, size_t N2>
1006 int strncmp(
1007 _In_ const utf32_t (&str1)[N1],
1008 _In_ const utf16_t (&str2)[N2])
1009 {
1010 return strncmp(str1, N1, str2, N2);
1011 }
1012
1021 template <class T1, class T2>
1022 int strrcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
1023 {
1024 size_t
1025 i = strlen(str1),
1026 j = strlen(str2);
1027 _Assume_(str1 || !i);
1028 _Assume_(str2 || !j);
1029 size_t k; T1 a; T2 b;
1030 for (k = 1; i && j; k++) {
1031 i--; j--;
1032 if ((a = str1[i]) > (b = str2[j])) return +1;
1033 if (a < b) return -1;
1034 }
1035 if (i && !j) return +1;
1036 if (!i && j) return -1;
1037 return 0;
1038 }
1039
1049 template <class T1, class T2>
1050 int strrncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
1051 {
1052 size_t
1053 i = strnlen(str1, count),
1054 j = strnlen(str2, count);
1055 _Assume_(str1 || !i);
1056 _Assume_(str2 || !j);
1057 size_t k; T1 a; T2 b;
1058 for (k = 1; i && j; k++) {
1059 i--; j--;
1060 if ((a = str1[i]) > (b = str2[j])) return +1;
1061 if (a < b) return -1;
1062 }
1063 if (i && !j) return +1;
1064 if (!i && j) return -1;
1065 return 0;
1066 }
1067
1078 template <class T1, class T2>
1079 int strrncmp(
1080 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1081 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1082 {
1083 size_t
1084 i = strnlen(str1, count1),
1085 j = strnlen(str2, count2);
1086 _Assume_(str1 || !i);
1087 _Assume_(str2 || !j);
1088 size_t k; T1 a; T2 b;
1089 for (k = 1; i && j; k++) {
1090 i--; j--;
1091 if ((a = str1[i]) > (b = str2[j])) return +1;
1092 if (a < b) return -1;
1093 }
1094 if (i && !j) return +1;
1095 if (!i && j) return -1;
1096 return 0;
1097 }
1098
1107 template <class T1, size_t N1, class T2, size_t N2>
1108 int strrncmp(
1109 _In_ const T1 (&str1)[N1],
1110 _In_ const T2 (&str2)[N2])
1111 {
1112 return strrncmp(str1, N1, str2, N2);
1113 }
1114
1123 template <class T1, class T2>
1124 int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
1125 {
1126 _Assume_(str1);
1127 _Assume_(str2);
1128 size_t i;
1129 for (i = 0; ; ++i) {
1130 auto a = tolower(str1[i]);
1131 auto b = tolower(str2[i]);
1132 if (!a && !b) return 0;
1133 if (a > b) return +1;
1134 if (a < b) return -1;
1135 }
1136 }
1137
1147 template <class T1, class T2>
1148 int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
1149 {
1150 _Assume_(str1);
1151 _Assume_(str2);
1152 size_t i;
1153 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1154 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1155 for (i = 0;; ++i) {
1156 auto a = ctype1.tolower(str1[i]);
1157 auto b = ctype2.tolower(str2[i]);
1158 if (!a && !b) return 0;
1159 if (a > b) return +1;
1160 if (a < b) return -1;
1161 }
1162 if (str1[i]) return +1;
1163 if (str2[i]) return -1;
1164 return 0;
1165 }
1166
1176 template <class T1, class T2>
1177 int strnicmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
1178 {
1179 _Assume_(str1 || !count);
1180 _Assume_(str2 || !count);
1181 size_t i;
1182 for (i = 0; i < count; ++i) {
1183 auto a = tolower(str1[i]);
1184 auto b = tolower(str2[i]);
1185 if (!a && !b) return 0;
1186 if (a > b) return +1;
1187 if (a < b) return -1;
1188 }
1189 if (i < count && str1[i]) return +1;
1190 if (i < count && str2[i]) return -1;
1191 return 0;
1192 }
1193
1204 template <class T1, class T2>
1205 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)
1206 {
1207 _Assume_(str1 || !count);
1208 _Assume_(str2 || !count);
1209 size_t i;
1210 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1211 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1212 for (i = 0; i < count; ++i) {
1213 auto a = ctype1.tolower(str1[i]);
1214 auto b = ctype2.tolower(str2[i]);
1215 if (!a && !b) return 0;
1216 if (a > b) return +1;
1217 if (a < b) return -1;
1218 }
1219 if (i < count && str1[i]) return +1;
1220 if (i < count && str2[i]) return -1;
1221 return 0;
1222 }
1223
1234 template <class T1, class T2>
1235 int strnicmp(
1236 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1237 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1238 {
1239 _Assume_(str1 || !count1);
1240 _Assume_(str2 || !count2);
1241 size_t i;
1242 for (i = 0; i < count1 && i < count2; ++i) {
1243 auto a = tolower(str1[i]);
1244 auto b = tolower(str2[i]);
1245 if (!a && !b) return 0;
1246 if (a > b) return +1;
1247 if (a < b) return -1;
1248 }
1249 if (i < count1 && str1[i]) return +1;
1250 if (i < count2 && str2[i]) return -1;
1251 return 0;
1252 }
1253
1265 template <class T1, class T2>
1266 int strnicmp(
1267 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1268 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
1269 _In_ const std::locale& locale)
1270 {
1271 _Assume_(str1 || !count1);
1272 _Assume_(str2 || !count2);
1273 size_t i;
1274 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1275 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1276 for (i = 0; i < count1 && i < count2; ++i) {
1277 auto a = ctype1.tolower(str1[i]);
1278 auto b = ctype2.tolower(str2[i]);
1279 if (!a && !b) return 0;
1280 if (a > b) return +1;
1281 if (a < b) return -1;
1282 }
1283 if (i < count1 && str1[i]) return +1;
1284 if (i < count2 && str2[i]) return -1;
1285 return 0;
1286 }
1287
1296 template <class T1, size_t N1, class T2, size_t N2>
1297 int strnicmp(
1298 _In_ const T1 (&str1)[N1],
1299 _In_ const T2 (&str2)[N2])
1300 {
1301 strnicmp(str1, N1, str2, N2);
1302 }
1303
1313 template <class T1, size_t N1, class T2, size_t N2>
1314 int strnicmp(
1315 _In_ const T1 (&str1)[N1],
1316 _In_ const T2 (&str2)[N2],
1317 _In_ const std::locale& locale)
1318 {
1319 strnicmp(str1, N1, str2, N2, locale);
1320 }
1321
1331 template <class T>
1332 int strcoll(
1333 _In_z_ const T* str1,
1334 _In_z_ const T* str2,
1335 _In_ const std::locale& locale)
1336 {
1337 _Assume_(str1);
1338 _Assume_(str2);
1339 auto& collate = std::use_facet<std::collate<T>>(locale);
1340 return collate.compare(str1, str1 + strlen(str1), str2, str2 + strlen(str2));
1341 }
1342
1354 template <class T>
1355 int strncoll(
1356 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
1357 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
1358 _In_ const std::locale& locale)
1359 {
1360 _Assume_(str1 || !count1);
1361 _Assume_(str2 || !count2);
1362 auto& collate = std::use_facet<std::collate<T>>(locale);
1363 return collate.compare(str1, str1 + count1, str2, str2 + count2);
1364 }
1365
1375 template <class T, size_t N1, size_t N2>
1376 int strncoll(
1377 _In_ const T (&str1)[N1],
1378 _In_ const T (&str2)[N2],
1379 _In_ const std::locale& locale)
1380 {
1381 return strncoll(str1, N1, str2, N2, locale);
1382 }
1383
1392 template <class T1, class T2>
1393 size_t strstr(
1394 _In_z_ const T1* str,
1395 _In_z_ const T2* sample)
1396 {
1397 _Assume_(str);
1398 _Assume_(sample);
1399 for (size_t offset = 0;; ++offset) {
1400 for (size_t i = offset, j = 0;; ++i, ++j) {
1401 if (!sample[j])
1402 return offset;
1403 if (!str[i])
1404 return npos;
1405 if (str[i] != sample[j])
1406 break;
1407 }
1408 }
1409 }
1410
1420 template <class T1, class T2>
1421 size_t strnstr(
1422 _In_reads_or_z_opt_(count) const T1* str, _In_ size_t count,
1423 _In_z_ const T2* sample)
1424 {
1425 _Assume_(str || !count);
1426 _Assume_(sample);
1427 for (size_t offset = 0;; ++offset) {
1428 for (size_t i = offset, j = 0;; ++i, ++j) {
1429 if (!sample[j])
1430 return offset;
1431 if (i >= count || !str[i])
1432 return npos;
1433 if (str[i] != sample[j])
1434 break;
1435 }
1436 }
1437 }
1438
1447 template <class T1, size_t N1, class T2>
1448 size_t strnstr(
1449 _In_ const T1 (&str)[N1],
1450 _In_z_ const T2* sample)
1451 {
1452 return strnstr(str, N1, sample);
1453 }
1454
1463 template <class T1, class T2>
1464 size_t stristr(
1465 _In_z_ const T1* str,
1466 _In_z_ const T2* sample)
1467 {
1468 _Assume_(str);
1469 _Assume_(sample);
1470 for (size_t offset = 0;; ++offset) {
1471 for (size_t i = offset, j = 0;; ++i, ++j) {
1472 if (!sample[j])
1473 return offset;
1474 if (!str[i])
1475 return npos;
1476 if (tolower(str[i]) != tolower(sample[j]))
1477 break;
1478 }
1479 }
1480 }
1481
1491 template <class T1, class T2>
1492 size_t stristr(
1493 _In_z_ const T1* str,
1494 _In_z_ const T2* sample,
1495 _In_ const std::locale& locale)
1496 {
1497 _Assume_(str);
1498 _Assume_(sample);
1499 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1500 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1501 for (size_t offset = 0;; ++offset) {
1502 for (size_t i = offset, j = 0;; ++i, ++j) {
1503 if (!sample[j])
1504 return offset;
1505 if (!str[i])
1506 return npos;
1507 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1508 break;
1509 }
1510 }
1511 }
1512
1522 template <class T1, class T2>
1523 size_t strnistr(
1524 _In_reads_or_z_opt_(count) const T1* str,
1525 _In_ size_t count,
1526 _In_z_ const T2* sample)
1527 {
1528 _Assume_(str || !count);
1529 _Assume_(sample);
1530 for (size_t offset = 0;; ++offset) {
1531 for (size_t i = offset, j = 0;; ++i, ++j) {
1532 if (!sample[j])
1533 return offset;
1534 if (i >= count || !str[i])
1535 return npos;
1536 if (tolower(str[i]) != tolower(sample[j]))
1537 break;
1538 }
1539 }
1540 }
1541
1552 template <class T1, class T2>
1553 size_t strnistr(
1554 _In_reads_or_z_opt_(count) const T1* str,
1555 _In_ size_t count,
1556 _In_z_ const T2* sample,
1557 _In_ const std::locale& locale)
1558 {
1559 _Assume_(str || !count);
1560 _Assume_(sample);
1561 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1562 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1563 for (size_t offset = 0;; ++offset) {
1564 for (size_t i = offset, j = 0;; ++i, ++j) {
1565 if (!sample[j])
1566 return offset;
1567 if (i >= count || !str[i])
1568 return npos;
1569 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1570 break;
1571 }
1572 }
1573 }
1574
1583 template <class T1, size_t N1, class T2>
1584 size_t strnistr(
1585 _In_ const T1 (&str)[N1],
1586 _In_z_ const T2* sample)
1587 {
1588 return strnistr(str, N1, sample);
1589 }
1590
1600 template <class T1, size_t N1, class T2>
1601 size_t strnistr(
1602 _In_ const T1 (&str)[N1],
1603 _In_z_ const T2* sample,
1604 _In_ const std::locale& locale)
1605 {
1606 return strnistr(str, N1, sample, locale);
1607 }
1608
1617 template <class T1, class T2>
1618 size_t strcpy(
1619 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
1620 _In_z_ const T2* src)
1621 {
1622 _Assume_(dst);
1623 _Assume_(src);
1624 for (size_t i = 0; ; ++i) {
1625 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1626 return i;
1627 }
1628 }
1629
1639 template <class T1, class T2>
1640 size_t strncpy(
1641 _Out_writes_(count) _Post_maybez_ T1* dst,
1642 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1643 {
1644 _Assume_(dst || !count);
1645 _Assume_(src || !count);
1646 for (size_t i = 0; ; ++i) {
1647 if (i >= count)
1648 return i;
1649 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1650 return i;
1651 }
1652 }
1653
1664 template <class T1, class T2>
1665 size_t strncpy(
1666 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1667 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1668 {
1669 _Assume_(dst || !count_dst);
1670 _Assume_(src || !count_src);
1671 for (size_t i = 0; ; ++i)
1672 {
1673 if (i >= count_dst)
1674 return i;
1675 if (i >= count_src) {
1676 dst[i] = 0;
1677 return i;
1678 }
1679 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1680 return i;
1681 }
1682 }
1683
1692 template <class T1, size_t N1, class T2, size_t N2>
1693 size_t strncpy(
1694 _Out_ _Post_maybez_ T1 (&dst)[N1],
1695 _In_ const T2 (&src)[N2])
1696 {
1697 return strncpy(dst, N1, src, N2);
1698 }
1699
1708 template <class T1, class T2>
1709 size_t strcat(
1710 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
1711 _In_z_ const T2* src)
1712 {
1713 _Assume_(dst);
1714 _Assume_(src);
1715 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1716 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1717 return j;
1718 }
1719 }
1720
1730 template <class T1, class T2>
1731 size_t strncat(
1732 _Inout_z_ T1* dst,
1733 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1734 {
1735 _Assume_(dst || !count);
1736 _Assume_(src || !count);
1737 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1738 if (i >= count)
1739 return j;
1740 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1741 return j;
1742 }
1743 }
1744
1755 template <class T1, class T2>
1756 size_t strncat(
1757 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1758 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1759 {
1760 _Assume_(dst || !count_dst);
1761 _Assume_(src || !count_src);
1762 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
1763 {
1764 if (j >= count_dst)
1765 return j;
1766 if (i >= count_src) {
1767 dst[j] = 0;
1768 return j;
1769 }
1770 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1771 return j;
1772 }
1773 }
1774
1785 template <class T>
1786 _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
1787 {
1788 if (!str) _Unlikely_
1789 return nullptr;
1790 size_t count = strlen(str) + 1;
1791 T* dst = new T[count];
1792 strncpy(dst, count, str, SIZE_MAX);
1793 return dst;
1794 }
1795
1807 template <class T>
1808 _Ret_z_ T* strndup(
1809 _In_reads_or_z_opt_(count) const T* str,
1810 _In_ size_t count)
1811 {
1812 T* dst = new T[count];
1813 strncpy(dst, count, str, SIZE_MAX);
1814 return dst;
1815 }
1816
1827 template <class T, size_t N>
1828 _Check_return_ _Ret_maybenull_z_ T* strndup(_In_ const T (&str)[N])
1829 {
1830 return strndup(str, N);
1831 }
1832
1842 template <class T>
1843 size_t crlf2nl(_Out_writes_z_(_String_length_(src) + 1) T* dst, _In_z_ const T* src)
1844 {
1845 _Assume_(dst);
1846 _Assume_(src);
1847 size_t i, j;
1848 for (i = j = 0; src[j];) {
1849 if (src[j] != '\r' || src[j + 1] != '\n')
1850 dst[i++] = src[j++];
1851 else {
1852 dst[i++] = '\n';
1853 j += 2;
1854 }
1855 }
1856 dst[i] = 0;
1857 return i;
1858 }
1859
1866 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1867 void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& dst, _In_z_ const T* src)
1868 {
1869 _Assume_(src);
1870 _Assume_(src != dst.data());
1871 dst.clear();
1872 dst.reserve(strlen(src));
1873 for (size_t j = 0; src[j];) {
1874 if (src[j] != '\r' || src[j + 1] != '\n')
1875 dst += src[j++];
1876 else {
1877 dst += '\n';
1878 j += 2;
1879 }
1880 }
1881 }
1882
1888 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1889 void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& str)
1890 {
1891 size_t i, j, n;
1892 for (i = j = 0, n = str.size(); j < n;) {
1893 if (str[j] != '\r' || str[j + 1] != '\n')
1894 str[i++] = str[j++];
1895 else {
1896 str[i++] = '\n';
1897 j += 2;
1898 }
1899 }
1900 str.resize(i);
1901 }
1902
1904 template <class T, class T_bin>
1905 T_bin strtoint(
1906 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1907 _Out_opt_ size_t* end,
1908 _In_ int radix,
1909 _Out_ uint8_t& flags)
1910 {
1911 _Assume_(str || !count);
1912 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
1913
1914 size_t i = 0;
1915 T_bin value = 0, digit,
1916 max_ui = (T_bin)-1,
1917 max_ui_pre1, max_ui_pre2;
1918
1919 flags = 0;
1920
1921 // Skip leading spaces.
1922 for (;; ++i) {
1923 if (i >= count || !str[i]) goto error;
1924 if (!isspace(str[i])) break;
1925 }
1926
1927 // Read the sign.
1928 if (str[i] == '+') {
1929 flags &= ~0x01;
1930 ++i;
1931 if (i >= count || !str[i]) goto error;
1932 }
1933 else if (str[i] == '-') {
1934 flags |= 0x01;
1935 ++i;
1936 if (i >= count || !str[i]) goto error;
1937 }
1938
1939 if (radix == 16) {
1940 // On hexadecimal, allow leading 0x.
1941 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
1942 i += 2;
1943 if (i >= count || !str[i]) goto error;
1944 }
1945 }
1946 else if (!radix) {
1947 // Autodetect radix.
1948 if (str[i] == '0') {
1949 ++i;
1950 if (i >= count || !str[i]) goto error;
1951 if (str[i] == 'x' || str[i] == 'X') {
1952 radix = 16;
1953 ++i;
1954 if (i >= count || !str[i]) goto error;
1955 }
1956 else
1957 radix = 8;
1958 }
1959 else
1960 radix = 10;
1961 }
1962
1963 // We have the radix.
1964 max_ui_pre1 = max_ui / (T_bin)radix;
1965 max_ui_pre2 = max_ui % (T_bin)radix;
1966 for (;;) {
1967 if ('0' <= str[i] && str[i] <= '9')
1968 digit = (T_bin)str[i] - '0';
1969 else if ('A' <= str[i] && str[i] <= 'Z')
1970 digit = (T_bin)str[i] - 'A' + '\x0a';
1971 else if ('a' <= str[i] && str[i] <= 'z')
1972 digit = (T_bin)str[i] - 'a' + '\x0a';
1973 else
1974 goto error;
1975 if (digit >= (T_bin)radix)
1976 goto error;
1977
1978 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
1979 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
1980 value = value * (T_bin)radix + digit;
1981 else {
1982 // Overflow!
1983 flags |= 0x02;
1984 }
1985
1986 ++i;
1987 if (i >= count || !str[i])
1988 goto error;
1989 }
1990
1991 error:
1992 if (end) *end = i;
1993 return value;
1994 }
1996
2007 template <class T, class T_bin>
2008 T_bin strtoint(
2009 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2010 _Out_opt_ size_t* end,
2011 _In_ int radix)
2012 {
2013 uint8_t flags;
2014 T_bin value;
2015
2016 switch (sizeof(T_bin)) {
2017 case 1:
2018 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
2019 if ((flags & 0x01) && (value & 0x80)) {
2020 // Sign bit is 1 => overflow.
2021 flags |= 0x02;
2022 }
2023 return (flags & 0x02) ?
2024 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
2025 (flags & 0x01) ? -value : value;
2026
2027 case 2:
2028 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
2029 if ((flags & 0x01) && (value & 0x8000)) {
2030 // Sign bit is 1 => overflow.
2031 flags |= 0x02;
2032 }
2033 return (flags & 0x02) ?
2034 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
2035 (flags & 0x01) ? -value : value;
2036
2037 case 4:
2038 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
2039 if ((flags & 0x01) && (value & 0x80000000)) {
2040 // Sign bit is 1 => overflow.
2041 flags |= 0x02;
2042 }
2043 return (flags & 0x02) ?
2044 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
2045 (flags & 0x01) ? -value : value;
2046
2047 case 8:
2048 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
2049 if ((flags & 0x01) && (value & 0x8000000000000000)) {
2050 // Sign bit is 1 => overflow.
2051 flags |= 0x02;
2052 }
2053 return (flags & 0x02) ?
2054 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
2055 (flags & 0x01) ? -value : value;
2056
2057 default:
2058 throw std::invalid_argument("Unsupported bit length");
2059 }
2060 }
2061
2071 template <class T, size_t N, class T_bin>
2072 T_bin strtoint(
2073 _In_ const T (&str)[N],
2074 _Out_opt_ size_t* end,
2075 _In_ int radix)
2076 {
2077 return strtoint<T, T_bin>(str, N, end, radix);
2078 }
2079
2090 template <class T, class T_bin>
2091 T_bin strtouint(
2092 _In_reads_or_z_opt_(count) const T* str,
2093 _In_ size_t count,
2094 _Out_opt_ size_t* end,
2095 _In_ int radix)
2096 {
2097 uint8_t flags;
2098 T_bin value;
2099
2100 switch (sizeof(T_bin)) {
2101 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
2102 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
2103 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
2104 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
2105 default: throw std::invalid_argument("Unsupported bit length");
2106 }
2107
2108 return (flags & 0x02) ?
2109 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
2110 (flags & 0x01) ? ~value : value;
2111 }
2112
2122 template <class T, size_t N, class T_bin>
2123 T_bin strtouint(
2124 _In_ const T (&str)[N],
2125 _Out_opt_ size_t* end,
2126 _In_ int radix)
2127 {
2128 return strtouint<T, T_bin>(str, N, end, radix);
2129 }
2130
2141 template <class T>
2142 int32_t strto32(
2143 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2144 _Out_opt_ size_t* end,
2145 _In_ int radix)
2146 {
2147 return strtoint<T, int32_t>(str, count, end, radix);
2148 }
2149
2159 template <class T, size_t N>
2160 int32_t strto32(
2161 _In_ const T (&str)[N],
2162 _Out_opt_ size_t* end,
2163 _In_ int radix)
2164 {
2165 return strto32<T>(str, N, end, radix);
2166 }
2167
2178 template <class T>
2179 int64_t strto64(
2180 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2181 _Out_opt_ size_t* end,
2182 _In_ int radix)
2183 {
2184 return strtoint<T, int64_t>(str, count, end, radix);
2185 }
2186
2196 template <class T, size_t N>
2197 int64_t strto64(
2198 _In_ const T (&str)[N],
2199 _Out_opt_ size_t* end,
2200 _In_ int radix)
2201 {
2202 return strto64<T>(str, N, end, radix);
2203 }
2204
2216 template <class T>
2217 ptrdiff_t strtoi(
2218 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2219 _Out_opt_ size_t* end,
2220 _In_ int radix)
2221 {
2222#if defined(_WIN64) || defined(__LP64__)
2223 return static_cast<ptrdiff_t>(strto64(str, count, end, radix));
2224#else
2225 return static_cast<ptrdiff_t>(strto32(str, count, end, radix));
2226#endif
2227 }
2228
2239 template <class T, size_t N>
2240 ptrdiff_t strtoi(
2241 _In_ const T (&str)[N],
2242 _Out_opt_ size_t* end,
2243 _In_ int radix)
2244 {
2245 return strtoi<T>(str, N, end, radix);
2246 }
2247
2258 template <class T>
2259 uint32_t strtou32(
2260 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2261 _Out_opt_ size_t* end,
2262 _In_ int radix)
2263 {
2264 return strtouint<T, uint32_t>(str, count, end, radix);
2265 }
2266
2276 template <class T, size_t N>
2277 uint32_t strtou32(
2278 _In_ const T (&str)[N],
2279 _Out_opt_ size_t* end,
2280 _In_ int radix)
2281 {
2282 return strtou32(str, N, end, radix);
2283 }
2284
2295 template <class T>
2296 uint64_t strtou64(
2297 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2298 _Out_opt_ size_t* end,
2299 _In_ int radix)
2300 {
2301 return strtouint<T, uint64_t>(str, count, end, radix);
2302 }
2303
2313 template <class T, size_t N>
2314 uint64_t strtou64(
2315 _In_ const T (&str)[N],
2316 _Out_opt_ size_t* end,
2317 _In_ int radix)
2318 {
2319 return strtou64<T>(str, N, end, radix);
2320 }
2321
2333 template <class T>
2334 size_t strtoui(
2335 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2336 _Out_opt_ size_t* end,
2337 _In_ int radix)
2338 {
2339#if defined(_WIN64) || defined(__LP64__)
2340 return static_cast<size_t>(strtou64(str, count, end, radix));
2341#else
2342 return static_cast<size_t>(strtou32(str, count, end, radix));
2343#endif
2344 }
2345
2356 template <class T, size_t N>
2357 size_t strtoui(
2358 _In_ const T (&str)[N],
2359 _Out_opt_ size_t* end,
2360 _In_ int radix)
2361 {
2362 return strtoui<T>(str, N, end, radix);
2363 }
2364
2375 inline double strtod(
2376 _In_reads_or_z_opt_(count) const char* str, _In_ size_t count,
2377 _Out_opt_ size_t* end,
2378 _In_opt_ locale_t locale)
2379 {
2380 count = strnlen(str, count);
2381 _Assume_(str || !count);
2382 std::string tmp(str, count);
2383 char* _end;
2384 double r;
2385#if _WIN32
2386 r = _strtod_l(tmp.c_str(), &_end, locale);
2387#else
2388 r = strtod_l(tmp.c_str(), &_end, locale);
2389#endif
2390 if (end) *end = (size_t)(_end - tmp.c_str());
2391 return r;
2392 }
2393
2404 inline double strtod(
2405 _In_reads_or_z_opt_(count) const wchar_t* str, _In_ size_t count,
2406 _Out_opt_ size_t* end,
2407 _In_opt_ locale_t locale)
2408 {
2409 count = strnlen(str, count);
2410 _Assume_(str || !count);
2411 std::wstring tmp(str, count);
2412 wchar_t* _end;
2413 double r;
2414#if _WIN32
2415 r = _wcstod_l(tmp.c_str(), &_end, locale);
2416#else
2417 r = wcstod_l(tmp.c_str(), &_end, locale);
2418#endif
2419 if (end) *end = (size_t)(_end - tmp.c_str());
2420 return r;
2421 }
2422
2424 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)
2425 {
2426#ifdef _WIN32
2427#pragma warning(suppress: 4996)
2428 return _vsnprintf_l(str, capacity, format, locale, arg);
2429#else
2430 va_list arg_mutable;
2431 va_copy(arg_mutable, arg);
2432 return ::vsnprintf_l(str, capacity, locale, format, arg_mutable);
2433#endif
2434 }
2435
2436 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)
2437 {
2438#ifdef _WIN32
2439#pragma warning(suppress: 4996)
2440 return _vsnwprintf_l(str, capacity, format, locale, arg);
2441#else
2442 va_list arg_mutable;
2443 va_copy(arg_mutable, arg);
2444 return ::vswprintf_l(str, capacity, locale, format, arg_mutable);
2445#endif
2446 }
2448
2459 template<class T, class TR, class AX>
2460 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)
2461 {
2462 T buf[1024 / sizeof(T)];
2463
2464 // Try with stack buffer first.
2465 int count = vsnprintf(buf, _countof(buf), format, locale, arg);
2466 if (0 <= count && static_cast<size_t>(count) <= _countof(buf)) {
2467 // Copy from stack.
2468 str.append(buf, static_cast<size_t>(count));
2469 return static_cast<size_t>(count);
2470 }
2471#ifdef _WIN32
2472 if (count < 0) {
2473 switch (errno) {
2474 case 0:
2475 count = vsnprintf(NULL, 0, format, locale, arg);
2476 _Assume_(count >= 0);
2477 break;
2478 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2479 case EILSEQ: throw std::runtime_error("encoding error");
2480 default: throw std::runtime_error("failed to format string");
2481 }
2482 }
2483 size_t offset = str.size();
2484 str.resize(offset + count);
2485 if (vsnprintf(&str[offset], count + 1, format, locale, arg) != count) _Unlikely_
2486 throw std::runtime_error("failed to format string");
2487#else
2488 size_t offset = str.size();
2489 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2490 switch (errno) {
2491 case EOVERFLOW:
2492 // Allocate on heap and retry.
2493 str.resize(offset + capacity);
2494 count = vsnprintf(&str[offset], capacity, format, locale, arg);
2495 if (0 <= count && static_cast<size_t>(count) <= capacity) {
2496 str.resize(offset + static_cast<size_t>(count));
2497 return static_cast<size_t>(count);
2498 }
2499 break;
2500 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2501 case EILSEQ: throw std::runtime_error("encoding error");
2502 default: throw std::runtime_error("failed to format string");
2503 }
2504 }
2505#endif
2506 return static_cast<size_t>(count);
2507 }
2508
2518 template<class T, class TR, class AX>
2519 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, ...)
2520 {
2521 va_list arg;
2522 va_start(arg, locale);
2523 size_t n = vappendf(str, format, locale, arg);
2524 va_end(arg);
2525 return n;
2526 }
2527
2536 template<class T, class TR, class AX>
2537 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)
2538 {
2539 str.clear();
2540 vappendf(str, format, locale, arg);
2541 }
2542
2550 template<class T, class TR, class AX>
2551 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, ...)
2552 {
2553 va_list arg;
2554 va_start(arg, locale);
2555 vsprintf(str, format, locale, arg);
2556 va_end(arg);
2557 }
2558
2568 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2569 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)
2570 {
2571 std::basic_string<T, TR, AX> str;
2572 vappendf(str, format, locale, arg);
2573 return str;
2574 }
2575
2584 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2585 std::basic_string<T, TR, AX> sprintf(_In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, ...)
2586 {
2587 va_list arg;
2588 va_start(arg, locale);
2589 auto str = vsprintf(format, locale, arg);
2590 va_end(arg);
2591 return str;
2592 }
2593
2595 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)
2596 {
2597#ifdef _WIN32
2598 return _strftime_l(str, capacity, format, time, locale);
2599#else
2600 return strftime_l(str, capacity, format, time, locale);
2601#endif
2602 }
2603
2604 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)
2605 {
2606#ifdef _WIN32
2607 return _wcsftime_l(str, capacity, format, time, locale);
2608#else
2609 return wcsftime_l(str, capacity, format, time, locale);
2610#endif
2611 }
2613
2622 template<class T, class TR, class AX>
2623 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)
2624 {
2625 T buf[1024 / sizeof(T)];
2626
2627 // Try with stack buffer first.
2628 size_t count = strftime(buf, _countof(buf), format, time, locale);
2629 if (count) {
2630 // Copy from stack.
2631 str.append(buf, count);
2632 return;
2633 }
2634 size_t offset = str.size();
2635 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2636 // Allocate on heap and retry.
2637 str.resize(offset + capacity);
2638 count = strftime(&str[offset], capacity + 1, format, time, locale);
2639 if (count) {
2640 str.resize(offset + count);
2641 return;
2642 }
2643 }
2644 }
2645
2654 template<class T, class TR, class AX>
2655 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)
2656 {
2657 str.clear();
2658 strcatftime(str, format, time, locale);
2659 }
2660
2670 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2671 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)
2672 {
2673 std::basic_string<T, TR, AX> str;
2674 strcatftime(str, format, time, locale);
2675 return str;
2676 }
2677
2683 //template<class T>
2684 //void strlwr(_Inout_z_ T* str)
2685 //{
2686 // _Assume_(str);
2687 // for (size_t i = 0; str[i]; ++i)
2688 // str[i] = tolower(str[i]);
2689 //}
2690
2697 //template<class T>
2698 //void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
2699 //{
2700 // _Assume_(str);
2701 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2702 // for (size_t i = 0; str[i]; ++i)
2703 // str[i] = ctype.tolower(str[i]);
2704 //}
2705
2712 template<class T>
2713 void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2714 {
2715 _Assume_(str || !count);
2716 for (size_t i = 0; i < count && str[i]; ++i)
2717 str[i] = tolower(str[i]);
2718 }
2719
2727 template<class T>
2728 void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2729 {
2730 _Assume_(str || !count);
2731 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2732 for (size_t i = 0; i < count && str[i]; ++i)
2733 str[i] = ctype.tolower(str[i]);
2734 }
2735
2741 template<class T, size_t N>
2742 void strlwr(_Inout_ T (&str)[N])
2743 {
2744 strlwr(str, N);
2745 }
2746
2753 template<class T, size_t N>
2754 void strlwr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2755 {
2756 strlwr(str, N, locale);
2757 }
2758
2764 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2765 void strlwr(_Inout_ std::basic_string<T, TR, AX>& str)
2766 {
2767 for (auto& c : str)
2768 c = tolower(c);
2769 }
2770
2777 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2778 void strlwr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2779 {
2780 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2781 for (auto& c : str)
2782 c = ctype.tolower(c);
2783 }
2784
2790 //template<class T>
2791 //void strupr(_Inout_z_ T* str)
2792 //{
2793 // _Assume_(str);
2794 // for (size_t i = 0; str[i]; ++i)
2795 // str[i] = toupper(str[i]);
2796 //}
2797
2804 //template<class T>
2805 //void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
2806 //{
2807 // _Assume_(str);
2808 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2809 // for (size_t i = 0; str[i]; ++i)
2810 // str[i] = ctype.toupper(str[i]);
2811 //}
2812
2819 template<class T>
2820 void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2821 {
2822 _Assume_(str || !count);
2823 for (size_t i = 0; i < count && str[i]; ++i)
2824 str[i] = toupper(str[i]);
2825 }
2826
2834 template<class T>
2835 void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2836 {
2837 _Assume_(str || !count);
2838 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2839 for (size_t i = 0; i < count && str[i]; ++i)
2840 str[i] = ctype.toupper(str[i]);
2841 }
2842
2848 template<class T, size_t N>
2849 void strupr(_Inout_ T (&str)[N])
2850 {
2851 return strupr(str, N);
2852 }
2853
2860 template<class T, size_t N>
2861 void strupr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2862 {
2863 return strupr(str, N, locale);
2864 }
2865
2871 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2872 void strupr(_Inout_ std::basic_string<T, TR, AX>& str)
2873 {
2874 for (auto& c : str)
2875 c = toupper(c);
2876 }
2877
2884 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2885 void strupr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2886 {
2887 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2888 for (auto& c : str)
2889 c = ctype.toupper(c);
2890 }
2891
2900 template<class T>
2901 size_t ltrim(
2902 _Inout_z_count_(count) T* str, _In_ size_t count)
2903 {
2904 for (size_t i = 0;; ++i) {
2905 if (i >= count) {
2906 if (count) str[0] = 0;
2907 return 0;
2908 }
2909 if (!str[i]) {
2910 str[0] = 0;
2911 return 0;
2912 }
2913 if (!isspace(str[i])) {
2914 if (!i)
2915 return strnlen(str, count);
2916 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2917 str[n] = 0;
2918 return n;
2919 }
2920 }
2921 }
2922
2932 template<class T>
2933 size_t ltrim(
2934 _Inout_z_count_(count) T* str, _In_ size_t count,
2935 _In_ const std::locale& locale)
2936 {
2937 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2938 for (size_t i = 0;; ++i) {
2939 if (i >= count) {
2940 if (count) str[0] = 0;
2941 return 0;
2942 }
2943 if (!str[i]) {
2944 str[0] = 0;
2945 return 0;
2946 }
2947 if (!ctype.is(ctype.space, str[i])) {
2948 if (!i)
2949 return strnlen(str, count);
2950 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2951 str[n] = 0;
2952 return n;
2953 }
2954 }
2955 }
2956
2962 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2963 void ltrim(_Inout_ std::basic_string<T, TR, AX>& s)
2964 {
2965 s.erase(
2966 s.begin(),
2967 std::find_if(
2968 s.begin(),
2969 s.end(),
2970 [&](_In_ T ch) { return !isspace(ch); }));
2971 }
2972
2979 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2980 void ltrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
2981 {
2982 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2983 s.erase(
2984 s.begin(),
2985 std::find_if(
2986 s.begin(),
2987 s.end(),
2988 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }));
2989 }
2990
2999 template<class T>
3000 size_t rtrim(
3001 _Inout_z_count_(count) T* str, _In_ size_t count)
3002 {
3003 for (size_t i = 0, j = 0;;) {
3004 if (i >= count || !str[i]) {
3005 if (j < count) str[j] = 0;
3006 return j;
3007 }
3008 if (!isspace(str[i]))
3009 j = ++i;
3010 else
3011 ++i;
3012 }
3013 }
3014
3024 template<class T>
3025 size_t rtrim(
3026 _Inout_z_count_(count) T* str, _In_ size_t count,
3027 _In_ const std::locale& locale)
3028 {
3029 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3030 for (size_t i = 0, j = 0;;) {
3031 if (i >= count || !str[i]) {
3032 if (j < count) str[j] = 0;
3033 return j;
3034 }
3035 if (!ctype.is(ctype.space, str[i]))
3036 j = ++i;
3037 else
3038 ++i;
3039 }
3040 }
3041
3047 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3048 void rtrim(_Inout_ std::basic_string<T, TR, AX>& s)
3049 {
3050 s.erase(
3051 std::find_if(
3052 s.rbegin(),
3053 s.rend(),
3054 [&](_In_ T ch) { return !isspace(ch); }).base(),
3055 s.end());
3056 }
3057
3064 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3065 void rtrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3066 {
3067 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3068 s.erase(
3069 std::find_if(
3070 s.rbegin(),
3071 s.rend(),
3072 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }).base(),
3073 s.end());
3074 }
3075
3084 template<class T>
3085 size_t trim(
3086 _Inout_z_count_(count) T* str, _In_ size_t count)
3087 {
3088 return ltrim(str, rtrim(str, count));
3089 }
3090
3100 template<class T>
3101 size_t trim(
3102 _Inout_z_count_(count) T* str, _In_ size_t count,
3103 _In_ const std::locale& locale)
3104 {
3105 return ltrim(str, rtrim(str, count, locale), locale);
3106 }
3107
3113 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3114 void trim(_Inout_ std::basic_string<T, TR, AX>& s)
3115 {
3116 auto nonspace = [&](_In_ T ch) { return !isspace(ch); };
3117 s.erase(
3118 s.begin(),
3119 std::find_if(
3120 s.begin(),
3121 s.end(),
3122 nonspace));
3123 s.erase(
3124 std::find_if(
3125 s.rbegin(),
3126 s.rend(),
3127 nonspace).base(),
3128 s.end());
3129 }
3130
3137 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3138 void trim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3139 {
3140 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3141 auto nonspace = [&](_In_ T ch) { return !ctype.is(ctype.space, ch); };
3142 s.erase(
3143 s.begin(),
3144 std::find_if(
3145 s.begin(),
3146 s.end(),
3147 nonspace));
3148 s.erase(
3149 std::find_if(
3150 s.rbegin(),
3151 s.rend(),
3152 nonspace).base(),
3153 s.end());
3154 }
3155}