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 "assert.hpp"
9#include "compat.hpp"
10#include "locale.hpp"
11#include <ctype.h>
12#include <stdarg.h>
13#include <stdarg.h>
14#include <stdint.h>
15#include <stdio.h>
16#include <time.h>
17#if defined(__APPLE__)
18#include <xlocale.h>
19#endif
20#include <algorithm>
21#include <climits>
22#include <locale>
23#include <stdexcept>
24
25namespace stdex
26{
30#ifdef _WIN32
31 using utf16_t = wchar_t;
32 using utf32_t = char32_t;
33#else
34 using utf16_t = char16_t;
35 using utf32_t = wchar_t;
36#endif
37
43 inline bool is_high_surrogate(_In_ utf16_t chr)
44 {
45 return 0xd800 < chr && chr < 0xdc00;
46 }
47
53 inline bool is_low_surrogate(_In_ utf16_t chr)
54 {
55 return 0xdc00 < chr && chr < 0xe000;
56 }
57
63 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
64 {
65 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
66 }
67
73 inline utf32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
74 {
75 stdex_assert(is_surrogate_pair(str));
76 return
77 (static_cast<utf32_t>(str[0] - 0xd800) << 10) +
78 static_cast<utf32_t>(str[1] - 0xdc00) +
79 0x10000;
80 }
81
87 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ utf32_t chr)
88 {
89 stdex_assert(chr >= 0x10000);
90 chr -= 0x10000;
91 str[0] = 0xd800 + static_cast<utf16_t>((chr >> 10) & 0x3ff);
92 str[1] = 0xdc00 + static_cast<utf16_t>(chr & 0x3ff);
93 }
94
100 inline bool iscombining(_In_ utf32_t chr)
101 {
102 return
103 (0x0300 <= chr && chr < 0x0370) ||
104 (0x1dc0 <= chr && chr < 0x1e00) ||
105 (0x20d0 <= chr && chr < 0x2100) ||
106 (0xfe20 <= chr && chr < 0xfe30);
107 }
108
114 template <class T>
115 bool islbreak(_In_ T chr)
116 {
117 return chr == '\n' || chr == '\r';
118 }
119
128 template <class T>
129 size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
130 {
131 stdex_assert(chr || !count);
132 if (count >= 2 && ((chr[0] == '\r' && chr[1] == '\n') || (chr[0] == '\n' && chr[1] == '\r')))
133 return 2;
134 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
135 return 1;
136 return 0;
137 }
138
144 template <class T>
145 bool isspace(_In_ T chr)
146 {
147 return chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' || chr == '\v' || chr == '\f';
148 }
149
155 template <class T>
156 bool islower(_In_ T chr)
157 {
158 return 'a' <= chr && chr <= 'z';
159 }
160
166 template <class T>
167 bool isupper(_In_ T chr)
168 {
169 return 'A' <= chr && chr <= 'Z';
170 }
171
177 template <class T>
178 bool isdigit(_In_ T chr)
179 {
180 return '0' <= chr && chr <= '9';
181 }
182
188 template <class T>
189 bool isalpha(_In_ T chr)
190 {
191 return islower(chr) || isupper(chr);
192 }
193
199 template <class T>
200 bool is7bit(_In_ T chr)
201 {
202 return '\x00' <= chr && chr <= '\x7f';
203 }
204
211 inline size_t glyphlen(_In_reads_or_z_opt_(count) const utf16_t* glyph, _In_ size_t count)
212 {
213 stdex_assert(glyph || !count);
214 if (count) {
215 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
216 for (; i < count && iscombining(glyph[i]); ++i);
217 return i;
218 }
219 return 0;
220 }
221
228 inline size_t glyphlen(_In_reads_or_z_opt_(count) const utf32_t* glyph, _In_ size_t count)
229 {
230 stdex_assert(glyph || !count);
231 if (count) {
232 size_t i = 1;
233 for (; i < count && iscombining(glyph[i]); ++i);
234 return i;
235 }
236 return 0;
237 }
238
245 inline size_t glyphrlen(_In_reads_or_z_opt_(count) const utf16_t* str, _In_ size_t count)
246 {
247 stdex_assert(count && str && str[count - 1]);
248 for (size_t i = count; i--;) {
249 if (!iscombining(str[i]))
250 return count - (!is_low_surrogate(str[i]) || i == 0 || !is_high_surrogate(str[i - 1]) ? i : i - 1);
251 }
252 return count;
253 }
254
261 inline size_t glyphrlen(_In_reads_or_z_opt_(count) const utf32_t* str, _In_ size_t count)
262 {
263 stdex_assert(count && str && str[count - 1]);
264 for (size_t i = count; i--;) {
265 if (!iscombining(str[i]))
266 return count - (i == 0 ? i : i - 1);
267 }
268 return count;
269 }
270
278 template <class T>
279 T tolower(_In_ T chr)
280 {
281 return isupper(chr) ? chr | 0x20 : chr;
282 }
283
291 template <class T>
292 T toupper(_In_ T chr)
293 {
294 return islower(chr) ? chr | ~0x20 : chr;
295 }
296
304 template <class T>
305 size_t strlen(_In_z_ const T* str)
306 {
307 stdex_assert(str);
308 size_t i;
309 for (i = 0; str[i]; ++i);
310 return i;
311 }
312
321 template <class T>
322 size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
323 {
324 stdex_assert(str || !count);
325 size_t i;
326 for (i = 0; i < count && str[i]; ++i);
327 return i;
328 }
329
337 template <class T, size_t N>
338 size_t strnlen(_In_ const T (&str)[N])
339 {
340 return strnlen(str, N);
341 }
342
343 constexpr auto npos{ static_cast<size_t>(-1) };
344
353 template <class T>
354 size_t strchr(_In_z_ const T* str, _In_ T chr)
355 {
356 stdex_assert(str);
357 for (size_t i = 0; str[i]; ++i)
358 if (str[i] == chr) return i;
359 return npos;
360 }
361
371 template <class T>
372 size_t strnchr(
373 _In_reads_or_z_opt_(count) const T* str,
374 _In_ size_t count,
375 _In_ T chr)
376 {
377 stdex_assert(str || !count);
378 for (size_t i = 0; i < count && str[i]; ++i)
379 if (str[i] == chr) return i;
380 return npos;
381 }
382
391 template <class T, size_t N>
392 size_t strnchr(
393 _In_ const T (&str)[N],
394 _In_ T chr)
395 {
396 return strnchr(str, N, chr);
397 }
398
407 template <class T>
408 size_t strrchr(
409 _In_z_ const T* str,
410 _In_ T chr)
411 {
412 stdex_assert(str);
413 size_t z = npos;
414 for (size_t i = 0; str[i]; ++i)
415 if (str[i] == chr) z = i;
416 return z;
417 }
418
428 template <class T>
429 size_t strrnchr(
430 _In_reads_or_z_opt_(count) const T* str,
431 _In_ size_t count,
432 _In_ T chr)
433 {
434 stdex_assert(str || !count);
435 size_t z = npos;
436 for (size_t i = 0; i < count && str[i]; ++i)
437 if (str[i] == chr) z = i;
438 return z;
439 }
440
449 template <class T, size_t N>
450 size_t strrnchr(
451 _In_ const T (&str)[N],
452 _In_ T chr)
453 {
454 return strrnchr(str, N, chr);
455 }
456
465 template <class T>
466 size_t strichr(
467 _In_z_ const T* str,
468 _In_ T chr)
469 {
470 stdex_assert(str);
471 chr = tolower(chr);
472 for (size_t i = 0; str[i]; ++i)
473 if (tolower(str[i]) == chr) return i;
474 return npos;
475 }
476
486 template <class T>
487 size_t strichr(
488 _In_z_ const T* str,
489 _In_ T chr,
490 _In_ const std::locale& locale)
491 {
492 stdex_assert(str);
493 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
494 chr = ctype.tolower(chr);
495 for (size_t i = 0; str[i]; ++i)
496 if (ctype.tolower(str[i]) == chr) return i;
497 return npos;
498 }
499
509 template <class T>
510 size_t strnichr(
511 _In_reads_or_z_opt_(count) const T* str,
512 _In_ size_t count,
513 _In_ T chr)
514 {
515 stdex_assert(str || !count);
516 chr = tolower(chr);
517 for (size_t i = 0; i < count && str[i]; ++i)
518 if (tolower(str[i]) == chr) return i;
519 return npos;
520 }
521
532 template <class T>
533 size_t strnichr(
534 _In_reads_or_z_opt_(count) const T* str,
535 _In_ size_t count,
536 _In_ T chr,
537 _In_ const std::locale& locale)
538 {
539 stdex_assert(str || !count);
540 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
541 chr = ctype.tolower(chr);
542 for (size_t i = 0; i < count && str[i]; ++i)
543 if (ctype.tolower(str[i]) == chr) return i;
544 return npos;
545 }
546
555 template <class T, size_t N>
556 size_t strnichr(
557 _In_ const T (&str)[N],
558 _In_ T chr)
559 {
560 return strnichr(str, N, chr);
561 }
562
572 template <class T, size_t N>
573 size_t strnichr(
574 _In_ const T (&str)[N],
575 _In_ T chr,
576 _In_ const std::locale& locale)
577 {
578 return strnichr(str, N, chr, locale);
579 }
580
589 template <class T>
590 size_t strrichr(
591 _In_z_ const T* str,
592 _In_ T chr)
593 {
594 stdex_assert(str);
595 chr = tolower(chr);
596 size_t z = npos;
597 for (size_t i = 0; str[i]; ++i)
598 if (tolower(str[i]) == chr) z = i;
599 return z;
600 }
601
611 template <class T>
612 size_t strrichr(
613 _In_reads_or_z_opt_(count) const T* str,
614 _In_ T chr,
615 _In_ const std::locale& locale)
616 {
617 stdex_assert(str);
618 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
619 chr = ctype.tolower(chr);
620 size_t z = npos;
621 for (size_t i = 0; str[i]; ++i)
622 if (ctype.tolower(str[i]) == chr) z = i;
623 return z;
624 }
625
635 template <class T>
636 size_t strrnichr(
637 _In_reads_or_z_opt_(count) const T* str,
638 _In_ size_t count,
639 _In_ T chr)
640 {
641 stdex_assert(str || !count);
642 chr = tolower(chr);
643 size_t z = npos;
644 for (size_t i = 0; i < count && str[i]; ++i)
645 if (tolower(str[i]) == chr) z = i;
646 return z;
647 }
648
659 template <class T>
660 size_t strrnichr(
661 _In_reads_or_z_opt_(count) const T* str,
662 _In_ size_t count,
663 _In_ T chr,
664 _In_ const std::locale& locale)
665 {
666 stdex_assert(str || !count);
667 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
668 chr = ctype.tolower(chr);
669 size_t z = npos;
670 for (size_t i = 0; i < count && str[i]; ++i)
671 if (ctype.tolower(str[i]) == chr) z = i;
672 return z;
673 }
674
683 template <class T, size_t N>
684 size_t strrnichr(
685 _In_ const T (&str)[N],
686 _In_ T chr)
687 {
688 return strrnichr(str, N, chr);
689 }
690
700 template <class T, size_t N>
701 size_t strrnichr(
702 _In_ const T (&str)[N],
703 _In_ T chr,
704 _In_ const std::locale& locale)
705 {
706 return strrnichr(str, N, chr, locale);
707 }
708
716 //template <class T>
717 //bool isblank(_In_z_ const T* str)
718 //{
719 // stdex_assert(str);
720 // for (size_t i = 0; str[i]; ++i)
721 // if (!isspace(str[i]))
722 // return false;
723 // return true;
724 //}
725
734 //template <class T>
735 //bool isblank(
736 // _In_z_ const T* str,
737 // _In_ const std::locale& locale)
738 //{
739 // stdex_assert(str);
740 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
741 // for (size_t i = 0; str[i]; ++i)
742 // if (!ctype.is(ctype.space, str[i]))
743 // return false;
744 // return true;
745 //}
746
755 template <class T>
756 bool isblank(
757 _In_reads_or_z_opt_(count) const T* str,
758 _In_ size_t count)
759 {
760 stdex_assert(str || !count);
761 for (size_t i = 0; i < count && str[i]; ++i)
762 if (!isspace(str[i]))
763 return false;
764 return true;
765 }
766
776 template <class T>
777 bool isblank(
778 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
779 _In_ const std::locale& locale)
780 {
781 stdex_assert(str || !count);
782 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
783 for (size_t i = 0; i < count && str[i]; ++i)
784 if (!ctype.is(ctype.space, str[i]))
785 return false;
786 return true;
787 }
788
796 template <class T, size_t N>
797 bool isblank(_In_ const T (&str)[N])
798 {
799 return isblank(str, N);
800 }
801
810 template <class T, size_t N>
811 bool isblank(
812 _In_ const T (&str)[N],
813 _In_ const std::locale& locale)
814 {
815 return isblank(str, N, locale);
816 }
817
818 // ///
819 // /// Checks if string contains all-ASCII characters
820 // ///
821 // /// \param[in] str String
822 // ///
823 // /// \return `true` if all characters are ASCII or `false` when any non-ASCII character is found in string.
824 // ///
825 // template <class T>
826 // bool is7bit(_In_z_ const T* str)
827 // {
828 // stdex_assert(str);
829 // for (size_t i = 0; str[i]; i++)
830 // if (!is7bit(str[i]))
831 // return false;
832 // return true;
833 // }
834
843 template <class T>
844 bool is7bit(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
845 {
846 stdex_assert(str || !count);
847 for (size_t i = 0; i < count && str[i]; i++)
848 if (!is7bit(str[i]))
849 return false;
850 return true;
851 }
852
860 template <class T, size_t N>
861 bool is7bit(_In_ const T (&str)[N])
862 {
863 return is7bit(str, N);
864 }
865
874 template <class T1, class T2>
875 int strcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
876 {
877 stdex_assert(str1);
878 stdex_assert(str2);
879 size_t i; T1 a; T2 b;
880 for (i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
881 if (a > b) return +1;
882 if (a < b) return -1;
883 }
884 if (str1[i]) return +1;
885 if (str2[i]) return -1;
886 return 0;
887 }
888
898 template <class T1, class T2>
899 int strncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
900 {
901 stdex_assert(str1 || !count);
902 stdex_assert(str2 || !count);
903 size_t i; T1 a; T2 b;
904 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
905 if (a > b) return +1;
906 if (a < b) return -1;
907 }
908 if (i < count && str1[i]) return +1;
909 if (i < count && str2[i]) return -1;
910 return 0;
911 }
912
923 template <class T1, class T2>
924 int strncmp(
925 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
926 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
927 {
928 stdex_assert(str1 || !count1);
929 stdex_assert(str2 || !count2);
930 size_t i;
931 for (i = 0; i < count1 && i < count2; ++i) {
932 auto a = str1[i];
933 auto b = str2[i];
934 if (!a && !b) return 0;
935 if (a > b) return +1;
936 if (a < b) return -1;
937 }
938 if (i < count1 && str1[i]) return +1;
939 if (i < count2 && str2[i]) return -1;
940 return 0;
941 }
942
951 template <class T1, size_t N1, class T2, size_t N2>
952 int strncmp(
953 _In_ const T1 (&str1)[N1],
954 _In_ const T2 (&str2)[N2])
955 {
956 return strncmp(str1, N1, str2, N2);
957 }
958
969 inline int strncmp(
970 _In_reads_or_z_opt_(count1) const utf32_t* str1, _In_ size_t count1,
971 _In_reads_or_z_opt_(count2) const utf16_t* str2, _In_ size_t count2)
972 {
973 stdex_assert(str1 || !count1);
974 stdex_assert(str2 || !count2);
975 size_t i, j, j_next; utf32_t a, b;
976 for (i = 0, j = 0; i < count1 && j < count2; ++i, j = j_next) {
977 a = str1[i];
978 if (!a)
979 break;
980 if (j + 1 >= count2 || !is_surrogate_pair(&str2[j])) {
981 b = static_cast<utf32_t>(str2[j]);
982 j_next = j + 1;
983 }
984 else {
985 b = surrogate_pair_to_ucs4(&str2[j]);
986 j_next = j + 2;
987 }
988 if (!b)
989 break;
990 if (a > b) return +1;
991 if (a < b) return -1;
992 }
993 if (i < count1 && str1[i]) return +1;
994 if (j < count2 && str2[j]) return -1;
995 return 0;
996 }
997
1006 template <size_t N1, size_t N2>
1007 int strncmp(
1008 _In_ const utf32_t (&str1)[N1],
1009 _In_ const utf16_t (&str2)[N2])
1010 {
1011 return strncmp(str1, N1, str2, N2);
1012 }
1013
1022 template <class T1, class T2>
1023 int strrcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
1024 {
1025 size_t
1026 i = strlen(str1),
1027 j = strlen(str2);
1028 stdex_assert(str1 || !i);
1029 stdex_assert(str2 || !j);
1030 size_t k; T1 a; T2 b;
1031 for (k = 1; i && j; k++) {
1032 i--; j--;
1033 if ((a = str1[i]) > (b = str2[j])) return +1;
1034 if (a < b) return -1;
1035 }
1036 if (i && !j) return +1;
1037 if (!i && j) return -1;
1038 return 0;
1039 }
1040
1050 template <class T1, class T2>
1051 int strrncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
1052 {
1053 size_t
1054 i = strnlen(str1, count),
1055 j = strnlen(str2, count);
1056 stdex_assert(str1 || !i);
1057 stdex_assert(str2 || !j);
1058 size_t k; T1 a; T2 b;
1059 for (k = 1; i && j; k++) {
1060 i--; j--;
1061 if ((a = str1[i]) > (b = str2[j])) return +1;
1062 if (a < b) return -1;
1063 }
1064 if (i && !j) return +1;
1065 if (!i && j) return -1;
1066 return 0;
1067 }
1068
1079 template <class T1, class T2>
1080 int strrncmp(
1081 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1082 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1083 {
1084 size_t
1085 i = strnlen(str1, count1),
1086 j = strnlen(str2, count2);
1087 stdex_assert(str1 || !i);
1088 stdex_assert(str2 || !j);
1089 size_t k; T1 a; T2 b;
1090 for (k = 1; i && j; k++) {
1091 i--; j--;
1092 if ((a = str1[i]) > (b = str2[j])) return +1;
1093 if (a < b) return -1;
1094 }
1095 if (i && !j) return +1;
1096 if (!i && j) return -1;
1097 return 0;
1098 }
1099
1108 template <class T1, size_t N1, class T2, size_t N2>
1109 int strrncmp(
1110 _In_ const T1 (&str1)[N1],
1111 _In_ const T2 (&str2)[N2])
1112 {
1113 return strrncmp(str1, N1, str2, N2);
1114 }
1115
1124 template <class T1, class T2>
1125 int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
1126 {
1127 stdex_assert(str1);
1128 stdex_assert(str2);
1129 size_t i;
1130 for (i = 0; ; ++i) {
1131 auto a = tolower(str1[i]);
1132 auto b = tolower(str2[i]);
1133 if (!a && !b) return 0;
1134 if (a > b) return +1;
1135 if (a < b) return -1;
1136 }
1137 }
1138
1148 template <class T1, class T2>
1149 int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
1150 {
1151 stdex_assert(str1);
1152 stdex_assert(str2);
1153 size_t i;
1154 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1155 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1156 for (i = 0;; ++i) {
1157 auto a = ctype1.tolower(str1[i]);
1158 auto b = ctype2.tolower(str2[i]);
1159 if (!a && !b) return 0;
1160 if (a > b) return +1;
1161 if (a < b) return -1;
1162 }
1163 }
1164
1174 template <class T1, class T2>
1175 int strnicmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
1176 {
1177 stdex_assert(str1 || !count);
1178 stdex_assert(str2 || !count);
1179 size_t i;
1180 for (i = 0; i < count; ++i) {
1181 auto a = tolower(str1[i]);
1182 auto b = tolower(str2[i]);
1183 if (!a && !b) return 0;
1184 if (a > b) return +1;
1185 if (a < b) return -1;
1186 }
1187 if (i < count && str1[i]) return +1;
1188 if (i < count && str2[i]) return -1;
1189 return 0;
1190 }
1191
1202 template <class T1, class T2>
1203 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)
1204 {
1205 stdex_assert(str1 || !count);
1206 stdex_assert(str2 || !count);
1207 size_t i;
1208 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1209 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1210 for (i = 0; i < count; ++i) {
1211 auto a = ctype1.tolower(str1[i]);
1212 auto b = ctype2.tolower(str2[i]);
1213 if (!a && !b) return 0;
1214 if (a > b) return +1;
1215 if (a < b) return -1;
1216 }
1217 if (i < count && str1[i]) return +1;
1218 if (i < count && str2[i]) return -1;
1219 return 0;
1220 }
1221
1232 template <class T1, class T2>
1233 int strnicmp(
1234 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1235 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1236 {
1237 stdex_assert(str1 || !count1);
1238 stdex_assert(str2 || !count2);
1239 size_t i;
1240 for (i = 0; i < count1 && i < count2; ++i) {
1241 auto a = tolower(str1[i]);
1242 auto b = tolower(str2[i]);
1243 if (!a && !b) return 0;
1244 if (a > b) return +1;
1245 if (a < b) return -1;
1246 }
1247 if (i < count1 && str1[i]) return +1;
1248 if (i < count2 && str2[i]) return -1;
1249 return 0;
1250 }
1251
1263 template <class T1, class T2>
1264 int strnicmp(
1265 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1266 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
1267 _In_ const std::locale& locale)
1268 {
1269 stdex_assert(str1 || !count1);
1270 stdex_assert(str2 || !count2);
1271 size_t i;
1272 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1273 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1274 for (i = 0; i < count1 && i < count2; ++i) {
1275 auto a = ctype1.tolower(str1[i]);
1276 auto b = ctype2.tolower(str2[i]);
1277 if (!a && !b) return 0;
1278 if (a > b) return +1;
1279 if (a < b) return -1;
1280 }
1281 if (i < count1 && str1[i]) return +1;
1282 if (i < count2 && str2[i]) return -1;
1283 return 0;
1284 }
1285
1294 template <class T1, size_t N1, class T2, size_t N2>
1295 int strnicmp(
1296 _In_ const T1 (&str1)[N1],
1297 _In_ const T2 (&str2)[N2])
1298 {
1299 strnicmp(str1, N1, str2, N2);
1300 }
1301
1311 template <class T1, size_t N1, class T2, size_t N2>
1312 int strnicmp(
1313 _In_ const T1 (&str1)[N1],
1314 _In_ const T2 (&str2)[N2],
1315 _In_ const std::locale& locale)
1316 {
1317 strnicmp(str1, N1, str2, N2, locale);
1318 }
1319
1329 template <class T>
1330 int strcoll(
1331 _In_z_ const T* str1,
1332 _In_z_ const T* str2,
1333 _In_ const std::locale& locale)
1334 {
1335 stdex_assert(str1);
1336 stdex_assert(str2);
1337 auto& collate = std::use_facet<std::collate<T>>(locale);
1338 return collate.compare(str1, str1 + strlen(str1), str2, str2 + strlen(str2));
1339 }
1340
1352 template <class T>
1353 int strncoll(
1354 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
1355 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
1356 _In_ const std::locale& locale)
1357 {
1358 stdex_assert(str1 || !count1);
1359 stdex_assert(str2 || !count2);
1360 auto& collate = std::use_facet<std::collate<T>>(locale);
1361 return collate.compare(str1, str1 + count1, str2, str2 + count2);
1362 }
1363
1373 template <class T, size_t N1, size_t N2>
1374 int strncoll(
1375 _In_ const T (&str1)[N1],
1376 _In_ const T (&str2)[N2],
1377 _In_ const std::locale& locale)
1378 {
1379 return strncoll(str1, N1, str2, N2, locale);
1380 }
1381
1390 template <class T1, class T2>
1391 size_t strstr(
1392 _In_z_ const T1* str,
1393 _In_z_ const T2* sample)
1394 {
1395 stdex_assert(str);
1396 stdex_assert(sample);
1397 for (size_t offset = 0;; ++offset) {
1398 for (size_t i = offset, j = 0;; ++i, ++j) {
1399 if (!sample[j])
1400 return offset;
1401 if (!str[i])
1402 return npos;
1403 if (str[i] != sample[j])
1404 break;
1405 }
1406 }
1407 }
1408
1418 template <class T1, class T2>
1419 size_t strnstr(
1420 _In_reads_or_z_opt_(count) const T1* str, _In_ size_t count,
1421 _In_z_ const T2* sample)
1422 {
1423 stdex_assert(str || !count);
1424 stdex_assert(sample);
1425 for (size_t offset = 0;; ++offset) {
1426 for (size_t i = offset, j = 0;; ++i, ++j) {
1427 if (!sample[j])
1428 return offset;
1429 if (i >= count || !str[i])
1430 return npos;
1431 if (str[i] != sample[j])
1432 break;
1433 }
1434 }
1435 }
1436
1445 template <class T1, size_t N1, class T2>
1446 size_t strnstr(
1447 _In_ const T1 (&str)[N1],
1448 _In_z_ const T2* sample)
1449 {
1450 return strnstr(str, N1, sample);
1451 }
1452
1461 template <class T1, class T2>
1462 size_t stristr(
1463 _In_z_ const T1* str,
1464 _In_z_ const T2* sample)
1465 {
1466 stdex_assert(str);
1467 stdex_assert(sample);
1468 for (size_t offset = 0;; ++offset) {
1469 for (size_t i = offset, j = 0;; ++i, ++j) {
1470 if (!sample[j])
1471 return offset;
1472 if (!str[i])
1473 return npos;
1474 if (tolower(str[i]) != tolower(sample[j]))
1475 break;
1476 }
1477 }
1478 }
1479
1489 template <class T1, class T2>
1490 size_t stristr(
1491 _In_z_ const T1* str,
1492 _In_z_ const T2* sample,
1493 _In_ const std::locale& locale)
1494 {
1495 stdex_assert(str);
1496 stdex_assert(sample);
1497 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1498 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1499 for (size_t offset = 0;; ++offset) {
1500 for (size_t i = offset, j = 0;; ++i, ++j) {
1501 if (!sample[j])
1502 return offset;
1503 if (!str[i])
1504 return npos;
1505 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1506 break;
1507 }
1508 }
1509 }
1510
1520 template <class T1, class T2>
1521 size_t strnistr(
1522 _In_reads_or_z_opt_(count) const T1* str,
1523 _In_ size_t count,
1524 _In_z_ const T2* sample)
1525 {
1526 stdex_assert(str || !count);
1527 stdex_assert(sample);
1528 for (size_t offset = 0;; ++offset) {
1529 for (size_t i = offset, j = 0;; ++i, ++j) {
1530 if (!sample[j])
1531 return offset;
1532 if (i >= count || !str[i])
1533 return npos;
1534 if (tolower(str[i]) != tolower(sample[j]))
1535 break;
1536 }
1537 }
1538 }
1539
1550 template <class T1, class T2>
1551 size_t strnistr(
1552 _In_reads_or_z_opt_(count) const T1* str,
1553 _In_ size_t count,
1554 _In_z_ const T2* sample,
1555 _In_ const std::locale& locale)
1556 {
1557 stdex_assert(str || !count);
1558 stdex_assert(sample);
1559 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1560 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1561 for (size_t offset = 0;; ++offset) {
1562 for (size_t i = offset, j = 0;; ++i, ++j) {
1563 if (!sample[j])
1564 return offset;
1565 if (i >= count || !str[i])
1566 return npos;
1567 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1568 break;
1569 }
1570 }
1571 }
1572
1581 template <class T1, size_t N1, class T2>
1582 size_t strnistr(
1583 _In_ const T1 (&str)[N1],
1584 _In_z_ const T2* sample)
1585 {
1586 return strnistr(str, N1, sample);
1587 }
1588
1598 template <class T1, size_t N1, class T2>
1599 size_t strnistr(
1600 _In_ const T1 (&str)[N1],
1601 _In_z_ const T2* sample,
1602 _In_ const std::locale& locale)
1603 {
1604 return strnistr(str, N1, sample, locale);
1605 }
1606
1615 template <class T1, class T2>
1616 size_t strcpy(
1617 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
1618 _In_z_ const T2* src)
1619 {
1620 stdex_assert(dst);
1621 stdex_assert(src);
1622 for (size_t i = 0; ; ++i) {
1623 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1624 return i;
1625 }
1626 }
1627
1637 template <class T1, class T2>
1638 size_t strncpy(
1639 _Out_writes_(count) _Post_maybez_ T1* dst,
1640 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1641 {
1642 stdex_assert(dst || !count);
1643 stdex_assert(src || !count);
1644 for (size_t i = 0; ; ++i) {
1645 if (i >= count)
1646 return i;
1647 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1648 return i;
1649 }
1650 }
1651
1662 template <class T1, class T2>
1663 size_t strncpy(
1664 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1665 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1666 {
1667 stdex_assert(dst || !count_dst);
1668 stdex_assert(src || !count_src);
1669 for (size_t i = 0; ; ++i)
1670 {
1671 if (i >= count_dst)
1672 return i;
1673 if (i >= count_src) {
1674 dst[i] = 0;
1675 return i;
1676 }
1677 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1678 return i;
1679 }
1680 }
1681
1690 template <class T1, size_t N1, class T2, size_t N2>
1691 size_t strncpy(
1692 _Out_ _Post_maybez_ T1 (&dst)[N1],
1693 _In_ const T2 (&src)[N2])
1694 {
1695 return strncpy(dst, N1, src, N2);
1696 }
1697
1706 template <class T1, class T2>
1707 size_t strcat(
1708 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
1709 _In_z_ const T2* src)
1710 {
1711 stdex_assert(dst);
1712 stdex_assert(src);
1713 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1714 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1715 return j;
1716 }
1717 }
1718
1728 template <class T1, class T2>
1729 size_t strncat(
1730 _Inout_z_ T1* dst,
1731 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1732 {
1733 stdex_assert(dst || !count);
1734 stdex_assert(src || !count);
1735 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1736 if (i >= count)
1737 return j;
1738 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1739 return j;
1740 }
1741 }
1742
1753 template <class T1, class T2>
1754 size_t strncat(
1755 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1756 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1757 {
1758 stdex_assert(dst || !count_dst);
1759 stdex_assert(src || !count_src);
1760 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
1761 {
1762 if (j >= count_dst)
1763 return j;
1764 if (i >= count_src) {
1765 dst[j] = 0;
1766 return j;
1767 }
1768 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1769 return j;
1770 }
1771 }
1772
1783 template <class T>
1784 _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
1785 {
1786 if (!str) _Unlikely_
1787 return nullptr;
1788 size_t count = strlen(str) + 1;
1789 T* dst = new T[count];
1790 strncpy(dst, count, str, SIZE_MAX);
1791 return dst;
1792 }
1793
1805 template <class T>
1806 _Ret_z_ T* strndup(
1807 _In_reads_or_z_opt_(count) const T* str,
1808 _In_ size_t count)
1809 {
1810 T* dst = new T[count];
1811 strncpy(dst, count, str, SIZE_MAX);
1812 return dst;
1813 }
1814
1825 template <class T, size_t N>
1826 _Check_return_ _Ret_maybenull_z_ T* strndup(_In_ const T (&str)[N])
1827 {
1828 return strndup(str, N);
1829 }
1830
1840 template <class T>
1841 size_t crlf2nl(_Out_writes_z_(_String_length_(src) + 1) T* dst, _In_z_ const T* src)
1842 {
1843 stdex_assert(dst);
1844 stdex_assert(src);
1845 size_t i, j;
1846 for (i = j = 0; src[j];) {
1847 if (src[j] != '\r' || src[j + 1] != '\n')
1848 dst[i++] = src[j++];
1849 else {
1850 dst[i++] = '\n';
1851 j += 2;
1852 }
1853 }
1854 dst[i] = 0;
1855 return i;
1856 }
1857
1864 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1865 void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& dst, _In_z_ const T* src)
1866 {
1867 stdex_assert(src);
1868 stdex_assert(src != dst.data());
1869 dst.clear();
1870 dst.reserve(strlen(src));
1871 for (size_t j = 0; src[j];) {
1872 if (src[j] != '\r' || src[j + 1] != '\n')
1873 dst += src[j++];
1874 else {
1875 dst += '\n';
1876 j += 2;
1877 }
1878 }
1879 }
1880
1886 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1887 void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& str)
1888 {
1889 size_t i, j, n;
1890 for (i = j = 0, n = str.size(); j < n;) {
1891 if (str[j] != '\r' || str[j + 1] != '\n')
1892 str[i++] = str[j++];
1893 else {
1894 str[i++] = '\n';
1895 j += 2;
1896 }
1897 }
1898 str.resize(i);
1899 }
1900
1902 template <class T, class T_bin>
1903 T_bin strtoint(
1904 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1905 _Out_opt_ size_t* end,
1906 _In_ int radix,
1907 _Out_ uint8_t& flags)
1908 {
1909 stdex_assert(str || !count);
1910 stdex_assert(radix == 0 || 2 <= radix && radix <= 36);
1911
1912 size_t i = 0;
1913 T_bin value = 0, digit,
1914 max_ui = (T_bin)-1,
1915 max_ui_pre1, max_ui_pre2;
1916
1917 flags = 0;
1918
1919 // Skip leading spaces.
1920 for (;; ++i) {
1921 if (i >= count || !str[i]) goto error;
1922 if (!isspace(str[i])) break;
1923 }
1924
1925 // Read the sign.
1926 if (str[i] == '+') {
1927 flags &= ~0x01;
1928 ++i;
1929 if (i >= count || !str[i]) goto error;
1930 }
1931 else if (str[i] == '-') {
1932 flags |= 0x01;
1933 ++i;
1934 if (i >= count || !str[i]) goto error;
1935 }
1936
1937 if (radix == 16) {
1938 // On hexadecimal, allow leading 0x.
1939 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
1940 i += 2;
1941 if (i >= count || !str[i]) goto error;
1942 }
1943 }
1944 else if (!radix) {
1945 // Autodetect radix.
1946 if (str[i] == '0') {
1947 ++i;
1948 if (i >= count || !str[i]) goto error;
1949 if (str[i] == 'x' || str[i] == 'X') {
1950 radix = 16;
1951 ++i;
1952 if (i >= count || !str[i]) goto error;
1953 }
1954 else
1955 radix = 8;
1956 }
1957 else
1958 radix = 10;
1959 }
1960
1961 // We have the radix.
1962 max_ui_pre1 = max_ui / (T_bin)radix;
1963 max_ui_pre2 = max_ui % (T_bin)radix;
1964 for (;;) {
1965 if ('0' <= str[i] && str[i] <= '9')
1966 digit = (T_bin)str[i] - '0';
1967 else if ('A' <= str[i] && str[i] <= 'Z')
1968 digit = (T_bin)str[i] - 'A' + '\x0a';
1969 else if ('a' <= str[i] && str[i] <= 'z')
1970 digit = (T_bin)str[i] - 'a' + '\x0a';
1971 else
1972 goto error;
1973 if (digit >= (T_bin)radix)
1974 goto error;
1975
1976 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
1977 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
1978 value = value * (T_bin)radix + digit;
1979 else {
1980 // Overflow!
1981 flags |= 0x02;
1982 }
1983
1984 ++i;
1985 if (i >= count || !str[i])
1986 goto error;
1987 }
1988
1989 error:
1990 if (end) *end = i;
1991 return value;
1992 }
1994
2005 template <class T, class T_bin>
2006 T_bin strtoint(
2007 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2008 _Out_opt_ size_t* end,
2009 _In_ int radix)
2010 {
2011 uint8_t flags;
2012 T_bin value;
2013
2014 switch (sizeof(T_bin)) {
2015 case 1:
2016 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
2017 if ((flags & 0x01) && (value & 0x80)) {
2018 // Sign bit is 1 => overflow.
2019 flags |= 0x02;
2020 }
2021 return (flags & 0x02) ?
2022 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
2023 (flags & 0x01) ? -value : value;
2024
2025 case 2:
2026 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
2027 if ((flags & 0x01) && (value & 0x8000)) {
2028 // Sign bit is 1 => overflow.
2029 flags |= 0x02;
2030 }
2031 return (flags & 0x02) ?
2032 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
2033 (flags & 0x01) ? -value : value;
2034
2035 case 4:
2036 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
2037 if ((flags & 0x01) && (value & 0x80000000)) {
2038 // Sign bit is 1 => overflow.
2039 flags |= 0x02;
2040 }
2041 return (flags & 0x02) ?
2042 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
2043 (flags & 0x01) ? -value : value;
2044
2045 case 8:
2046 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
2047 if ((flags & 0x01) && (value & 0x8000000000000000)) {
2048 // Sign bit is 1 => overflow.
2049 flags |= 0x02;
2050 }
2051 return (flags & 0x02) ?
2052 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
2053 (flags & 0x01) ? -value : value;
2054
2055 default:
2056 throw std::invalid_argument("Unsupported bit length");
2057 }
2058 }
2059
2069 template <class T, size_t N, class T_bin>
2070 T_bin strtoint(
2071 _In_ const T (&str)[N],
2072 _Out_opt_ size_t* end,
2073 _In_ int radix)
2074 {
2075 return strtoint<T, T_bin>(str, N, end, radix);
2076 }
2077
2088 template <class T, class T_bin>
2089 T_bin strtouint(
2090 _In_reads_or_z_opt_(count) const T* str,
2091 _In_ size_t count,
2092 _Out_opt_ size_t* end,
2093 _In_ int radix)
2094 {
2095 uint8_t flags;
2096 T_bin value;
2097
2098 switch (sizeof(T_bin)) {
2099 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
2100 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
2101 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
2102 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
2103 default: throw std::invalid_argument("Unsupported bit length");
2104 }
2105
2106 return (flags & 0x02) ?
2107 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
2108 (flags & 0x01) ? ~value : value;
2109 }
2110
2120 template <class T, size_t N, class T_bin>
2121 T_bin strtouint(
2122 _In_ const T (&str)[N],
2123 _Out_opt_ size_t* end,
2124 _In_ int radix)
2125 {
2126 return strtouint<T, T_bin>(str, N, end, radix);
2127 }
2128
2139 template <class T>
2140 int32_t strto32(
2141 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2142 _Out_opt_ size_t* end,
2143 _In_ int radix)
2144 {
2145 return strtoint<T, int32_t>(str, count, end, radix);
2146 }
2147
2157 template <class T, size_t N>
2158 int32_t strto32(
2159 _In_ const T (&str)[N],
2160 _Out_opt_ size_t* end,
2161 _In_ int radix)
2162 {
2163 return strto32<T>(str, N, end, radix);
2164 }
2165
2176 template <class T>
2177 int64_t strto64(
2178 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2179 _Out_opt_ size_t* end,
2180 _In_ int radix)
2181 {
2182 return strtoint<T, int64_t>(str, count, end, radix);
2183 }
2184
2194 template <class T, size_t N>
2195 int64_t strto64(
2196 _In_ const T (&str)[N],
2197 _Out_opt_ size_t* end,
2198 _In_ int radix)
2199 {
2200 return strto64<T>(str, N, end, radix);
2201 }
2202
2214 template <class T>
2215 ptrdiff_t strtoi(
2216 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2217 _Out_opt_ size_t* end,
2218 _In_ int radix)
2219 {
2220#if defined(_WIN64) || defined(__LP64__)
2221 return static_cast<ptrdiff_t>(strto64(str, count, end, radix));
2222#else
2223 return static_cast<ptrdiff_t>(strto32(str, count, end, radix));
2224#endif
2225 }
2226
2237 template <class T, size_t N>
2238 ptrdiff_t strtoi(
2239 _In_ const T (&str)[N],
2240 _Out_opt_ size_t* end,
2241 _In_ int radix)
2242 {
2243 return strtoi<T>(str, N, end, radix);
2244 }
2245
2256 template <class T>
2257 uint32_t strtou32(
2258 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2259 _Out_opt_ size_t* end,
2260 _In_ int radix)
2261 {
2262 return strtouint<T, uint32_t>(str, count, end, radix);
2263 }
2264
2274 template <class T, size_t N>
2275 uint32_t strtou32(
2276 _In_ const T (&str)[N],
2277 _Out_opt_ size_t* end,
2278 _In_ int radix)
2279 {
2280 return strtou32(str, N, end, radix);
2281 }
2282
2293 template <class T>
2294 uint64_t strtou64(
2295 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2296 _Out_opt_ size_t* end,
2297 _In_ int radix)
2298 {
2299 return strtouint<T, uint64_t>(str, count, end, radix);
2300 }
2301
2311 template <class T, size_t N>
2312 uint64_t strtou64(
2313 _In_ const T (&str)[N],
2314 _Out_opt_ size_t* end,
2315 _In_ int radix)
2316 {
2317 return strtou64<T>(str, N, end, radix);
2318 }
2319
2331 template <class T>
2332 size_t strtoui(
2333 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2334 _Out_opt_ size_t* end,
2335 _In_ int radix)
2336 {
2337#if defined(_WIN64) || defined(__LP64__)
2338 return static_cast<size_t>(strtou64(str, count, end, radix));
2339#else
2340 return static_cast<size_t>(strtou32(str, count, end, radix));
2341#endif
2342 }
2343
2354 template <class T, size_t N>
2355 size_t strtoui(
2356 _In_ const T (&str)[N],
2357 _Out_opt_ size_t* end,
2358 _In_ int radix)
2359 {
2360 return strtoui<T>(str, N, end, radix);
2361 }
2362
2373 inline double strtod(
2374 _In_reads_or_z_opt_(count) const char* str, _In_ size_t count,
2375 _Out_opt_ size_t* end,
2376 _In_opt_ locale_t locale)
2377 {
2378 count = strnlen(str, count);
2379 stdex_assert(str || !count);
2380 std::string tmp(str, count);
2381 char* _end;
2382 double r;
2383#if _WIN32
2384 r = _strtod_l(tmp.c_str(), &_end, locale);
2385#else
2386 r = strtod_l(tmp.c_str(), &_end, locale);
2387#endif
2388 if (end) *end = (size_t)(_end - tmp.c_str());
2389 return r;
2390 }
2391
2402 inline double strtod(
2403 _In_reads_or_z_opt_(count) const wchar_t* str, _In_ size_t count,
2404 _Out_opt_ size_t* end,
2405 _In_opt_ locale_t locale)
2406 {
2407 count = strnlen(str, count);
2408 stdex_assert(str || !count);
2409 std::wstring tmp(str, count);
2410 wchar_t* _end;
2411 double r;
2412#if _WIN32
2413 r = _wcstod_l(tmp.c_str(), &_end, locale);
2414#else
2415 r = wcstod_l(tmp.c_str(), &_end, locale);
2416#endif
2417 if (end) *end = (size_t)(_end - tmp.c_str());
2418 return r;
2419 }
2420
2422 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)
2423 {
2424#ifdef _WIN32
2425#pragma warning(suppress: 4996)
2426 return _vsnprintf_l(str, capacity, format, locale, arg);
2427#else
2428 va_list arg_mutable;
2429 va_copy(arg_mutable, arg);
2430 return ::vsnprintf_l(str, capacity, locale, format, arg_mutable);
2431#endif
2432 }
2433
2434 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)
2435 {
2436#ifdef _WIN32
2437#pragma warning(suppress: 4996)
2438 return _vsnwprintf_l(str, capacity, format, locale, arg);
2439#else
2440 va_list arg_mutable;
2441 va_copy(arg_mutable, arg);
2442 return ::vswprintf_l(str, capacity, locale, format, arg_mutable);
2443#endif
2444 }
2446
2457 template<class T, class TR, class AX>
2458 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)
2459 {
2460 T buf[1024 / sizeof(T)];
2461
2462 // Try with stack buffer first.
2463 int count = vsnprintf(buf, _countof(buf), format, locale, arg);
2464 if (0 <= count && static_cast<size_t>(count) <= _countof(buf)) {
2465 // Copy from stack.
2466 str.append(buf, static_cast<size_t>(count));
2467 return static_cast<size_t>(count);
2468 }
2469#ifdef _WIN32
2470 if (count < 0) {
2471 switch (errno) {
2472 case 0:
2473 count = vsnprintf(NULL, 0, format, locale, arg);
2474 stdex_assert(count >= 0);
2475 break;
2476 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2477 case EILSEQ: throw std::runtime_error("encoding error");
2478 default: throw std::runtime_error("failed to format string");
2479 }
2480 }
2481 size_t offset = str.size();
2482 str.resize(offset + count);
2483 if (vsnprintf(&str[offset], count + 1, format, locale, arg) != count) _Unlikely_
2484 throw std::runtime_error("failed to format string");
2485#else
2486 size_t offset = str.size();
2487 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2488 switch (errno) {
2489 case EOVERFLOW:
2490 // Allocate on heap and retry.
2491 str.resize(offset + capacity);
2492 count = vsnprintf(&str[offset], capacity, format, locale, arg);
2493 if (0 <= count && static_cast<size_t>(count) <= capacity) {
2494 str.resize(offset + static_cast<size_t>(count));
2495 return static_cast<size_t>(count);
2496 }
2497 break;
2498 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2499 case EILSEQ: throw std::runtime_error("encoding error");
2500 default: throw std::runtime_error("failed to format string");
2501 }
2502 }
2503#endif
2504 return static_cast<size_t>(count);
2505 }
2506
2516 template<class T, class TR, class AX>
2517 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, ...)
2518 {
2519 va_list arg;
2520 va_start(arg, locale);
2521 size_t n = vappendf(str, format, locale, arg);
2522 va_end(arg);
2523 return n;
2524 }
2525
2534 template<class T, class TR, class AX>
2535 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)
2536 {
2537 str.clear();
2538 vappendf(str, format, locale, arg);
2539 }
2540
2548 template<class T, class TR, class AX>
2549 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, ...)
2550 {
2551 va_list arg;
2552 va_start(arg, locale);
2553 vsprintf(str, format, locale, arg);
2554 va_end(arg);
2555 }
2556
2566 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2567 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)
2568 {
2569 std::basic_string<T, TR, AX> str;
2570 vappendf(str, format, locale, arg);
2571 return str;
2572 }
2573
2582 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2583 std::basic_string<T, TR, AX> sprintf(_In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, ...)
2584 {
2585 va_list arg;
2586 va_start(arg, locale);
2587 auto str = vsprintf(format, locale, arg);
2588 va_end(arg);
2589 return str;
2590 }
2591
2593 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)
2594 {
2595#ifdef _WIN32
2596 return _strftime_l(str, capacity, format, time, locale);
2597#else
2598 return strftime_l(str, capacity, format, time, locale);
2599#endif
2600 }
2601
2602 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)
2603 {
2604#ifdef _WIN32
2605 return _wcsftime_l(str, capacity, format, time, locale);
2606#else
2607 return wcsftime_l(str, capacity, format, time, locale);
2608#endif
2609 }
2611
2620 template<class T, class TR, class AX>
2621 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)
2622 {
2623 T buf[1024 / sizeof(T)];
2624
2625 // Try with stack buffer first.
2626 size_t count = strftime(buf, _countof(buf), format, time, locale);
2627 if (count) {
2628 // Copy from stack.
2629 str.append(buf, count);
2630 return;
2631 }
2632 size_t offset = str.size();
2633 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2634 // Allocate on heap and retry.
2635 str.resize(offset + capacity);
2636 count = strftime(&str[offset], capacity + 1, format, time, locale);
2637 if (count) {
2638 str.resize(offset + count);
2639 return;
2640 }
2641 }
2642 }
2643
2652 template<class T, class TR, class AX>
2653 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)
2654 {
2655 str.clear();
2656 strcatftime(str, format, time, locale);
2657 }
2658
2668 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2669 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)
2670 {
2671 std::basic_string<T, TR, AX> str;
2672 strcatftime(str, format, time, locale);
2673 return str;
2674 }
2675
2681 //template<class T>
2682 //void strlwr(_Inout_z_ T* str)
2683 //{
2684 // stdex_assert(str);
2685 // for (size_t i = 0; str[i]; ++i)
2686 // str[i] = tolower(str[i]);
2687 //}
2688
2695 //template<class T>
2696 //void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
2697 //{
2698 // stdex_assert(str);
2699 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2700 // for (size_t i = 0; str[i]; ++i)
2701 // str[i] = ctype.tolower(str[i]);
2702 //}
2703
2710 template<class T>
2711 void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2712 {
2713 stdex_assert(str || !count);
2714 for (size_t i = 0; i < count && str[i]; ++i)
2715 str[i] = tolower(str[i]);
2716 }
2717
2725 template<class T>
2726 void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2727 {
2728 stdex_assert(str || !count);
2729 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2730 for (size_t i = 0; i < count && str[i]; ++i)
2731 str[i] = ctype.tolower(str[i]);
2732 }
2733
2739 template<class T, size_t N>
2740 void strlwr(_Inout_ T (&str)[N])
2741 {
2742 strlwr(str, N);
2743 }
2744
2751 template<class T, size_t N>
2752 void strlwr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2753 {
2754 strlwr(str, N, locale);
2755 }
2756
2762 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2763 void strlwr(_Inout_ std::basic_string<T, TR, AX>& str)
2764 {
2765 for (auto& c : str)
2766 c = tolower(c);
2767 }
2768
2775 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2776 void strlwr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2777 {
2778 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2779 for (auto& c : str)
2780 c = ctype.tolower(c);
2781 }
2782
2788 //template<class T>
2789 //void strupr(_Inout_z_ T* str)
2790 //{
2791 // stdex_assert(str);
2792 // for (size_t i = 0; str[i]; ++i)
2793 // str[i] = toupper(str[i]);
2794 //}
2795
2802 //template<class T>
2803 //void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
2804 //{
2805 // stdex_assert(str);
2806 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2807 // for (size_t i = 0; str[i]; ++i)
2808 // str[i] = ctype.toupper(str[i]);
2809 //}
2810
2817 template<class T>
2818 void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2819 {
2820 stdex_assert(str || !count);
2821 for (size_t i = 0; i < count && str[i]; ++i)
2822 str[i] = toupper(str[i]);
2823 }
2824
2832 template<class T>
2833 void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2834 {
2835 stdex_assert(str || !count);
2836 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2837 for (size_t i = 0; i < count && str[i]; ++i)
2838 str[i] = ctype.toupper(str[i]);
2839 }
2840
2846 template<class T, size_t N>
2847 void strupr(_Inout_ T (&str)[N])
2848 {
2849 return strupr(str, N);
2850 }
2851
2858 template<class T, size_t N>
2859 void strupr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2860 {
2861 return strupr(str, N, locale);
2862 }
2863
2869 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2870 void strupr(_Inout_ std::basic_string<T, TR, AX>& str)
2871 {
2872 for (auto& c : str)
2873 c = toupper(c);
2874 }
2875
2882 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2883 void strupr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2884 {
2885 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2886 for (auto& c : str)
2887 c = ctype.toupper(c);
2888 }
2889
2898 template<class T>
2899 size_t ltrim(
2900 _Inout_z_count_(count) T* str, _In_ size_t count)
2901 {
2902 for (size_t i = 0;; ++i) {
2903 if (i >= count) {
2904 if (count) str[0] = 0;
2905 return 0;
2906 }
2907 if (!str[i]) {
2908 str[0] = 0;
2909 return 0;
2910 }
2911 if (!isspace(str[i])) {
2912 if (!i)
2913 return strnlen(str, count);
2914 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2915 str[n] = 0;
2916 return n;
2917 }
2918 }
2919 }
2920
2930 template<class T>
2931 size_t ltrim(
2932 _Inout_z_count_(count) T* str, _In_ size_t count,
2933 _In_ const std::locale& locale)
2934 {
2935 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2936 for (size_t i = 0;; ++i) {
2937 if (i >= count) {
2938 if (count) str[0] = 0;
2939 return 0;
2940 }
2941 if (!str[i]) {
2942 str[0] = 0;
2943 return 0;
2944 }
2945 if (!ctype.is(ctype.space, str[i])) {
2946 if (!i)
2947 return strnlen(str, count);
2948 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2949 str[n] = 0;
2950 return n;
2951 }
2952 }
2953 }
2954
2960 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2961 void ltrim(_Inout_ std::basic_string<T, TR, AX>& s)
2962 {
2963 s.erase(
2964 s.begin(),
2965 std::find_if(
2966 s.begin(),
2967 s.end(),
2968 [&](_In_ T ch) { return !isspace(ch); }));
2969 }
2970
2977 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2978 void ltrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
2979 {
2980 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2981 s.erase(
2982 s.begin(),
2983 std::find_if(
2984 s.begin(),
2985 s.end(),
2986 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }));
2987 }
2988
2997 template<class T>
2998 size_t rtrim(
2999 _Inout_z_count_(count) T* str, _In_ size_t count)
3000 {
3001 for (size_t i = 0, j = 0;;) {
3002 if (i >= count || !str[i]) {
3003 if (j < count) str[j] = 0;
3004 return j;
3005 }
3006 if (!isspace(str[i]))
3007 j = ++i;
3008 else
3009 ++i;
3010 }
3011 }
3012
3022 template<class T>
3023 size_t rtrim(
3024 _Inout_z_count_(count) T* str, _In_ size_t count,
3025 _In_ const std::locale& locale)
3026 {
3027 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3028 for (size_t i = 0, j = 0;;) {
3029 if (i >= count || !str[i]) {
3030 if (j < count) str[j] = 0;
3031 return j;
3032 }
3033 if (!ctype.is(ctype.space, str[i]))
3034 j = ++i;
3035 else
3036 ++i;
3037 }
3038 }
3039
3045 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3046 void rtrim(_Inout_ std::basic_string<T, TR, AX>& s)
3047 {
3048 s.erase(
3049 std::find_if(
3050 s.rbegin(),
3051 s.rend(),
3052 [&](_In_ T ch) { return !isspace(ch); }).base(),
3053 s.end());
3054 }
3055
3062 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3063 void rtrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3064 {
3065 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3066 s.erase(
3067 std::find_if(
3068 s.rbegin(),
3069 s.rend(),
3070 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }).base(),
3071 s.end());
3072 }
3073
3082 template<class T>
3083 size_t trim(
3084 _Inout_z_count_(count) T* str, _In_ size_t count)
3085 {
3086 return ltrim(str, rtrim(str, count));
3087 }
3088
3098 template<class T>
3099 size_t trim(
3100 _Inout_z_count_(count) T* str, _In_ size_t count,
3101 _In_ const std::locale& locale)
3102 {
3103 return ltrim(str, rtrim(str, count, locale), locale);
3104 }
3105
3111 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3112 void trim(_Inout_ std::basic_string<T, TR, AX>& s)
3113 {
3114 auto nonspace = [&](_In_ T ch) { return !isspace(ch); };
3115 s.erase(
3116 s.begin(),
3117 std::find_if(
3118 s.begin(),
3119 s.end(),
3120 nonspace));
3121 s.erase(
3122 std::find_if(
3123 s.rbegin(),
3124 s.rend(),
3125 nonspace).base(),
3126 s.end());
3127 }
3128
3135 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3136 void trim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3137 {
3138 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3139 auto nonspace = [&](_In_ T ch) { return !ctype.is(ctype.space, ch); };
3140 s.erase(
3141 s.begin(),
3142 std::find_if(
3143 s.begin(),
3144 s.end(),
3145 nonspace));
3146 s.erase(
3147 std::find_if(
3148 s.rbegin(),
3149 s.rend(),
3150 nonspace).base(),
3151 s.end());
3152 }
3153}