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