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#ifdef __APPLE__
18#include <xlocale.h>
19#endif
20#include <algorithm>
21#include <climits>
22#include <locale>
23#include <stdexcept>
24
25 namespace 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 ispunct(_In_ T chr)
157 {
158 return ('!' <= chr && chr <= '/') || (':' <= chr && chr <= '@') || ('[' <= chr && chr <= '`') || ('{' <= chr && chr <= '~');
159 }
160
166 template <class T>
167 bool islower(_In_ T chr)
168 {
169 return 'a' <= chr && chr <= 'z';
170 }
171
177 template <class T>
178 bool isupper(_In_ T chr)
179 {
180 return 'A' <= chr && chr <= 'Z';
181 }
182
188 template <class T>
189 bool isdigit(_In_ T chr)
190 {
191 return '0' <= chr && chr <= '9';
192 }
193
199 template <class T>
200 bool isalpha(_In_ T chr)
201 {
202 return islower(chr) || isupper(chr);
203 }
204
210 template <class T>
211 bool is7bit(_In_ T chr)
212 {
213 return '\x00' <= chr && chr <= '\x7f';
214 }
215
222 inline size_t glyphlen(_In_reads_or_z_opt_(count) const utf16_t* glyph, _In_ size_t count)
223 {
224 stdex_assert(glyph || !count);
225 if (count) {
226 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
227 for (; i < count && iscombining(glyph[i]); ++i);
228 return i;
229 }
230 return 0;
231 }
232
239 inline size_t glyphlen(_In_reads_or_z_opt_(count) const utf32_t* glyph, _In_ size_t count)
240 {
241 stdex_assert(glyph || !count);
242 if (count) {
243 size_t i = 1;
244 for (; i < count && iscombining(glyph[i]); ++i);
245 return i;
246 }
247 return 0;
248 }
249
256 inline size_t glyphrlen(_In_reads_or_z_opt_(count) const utf16_t* str, _In_ size_t count)
257 {
258 stdex_assert(count && str && str[count - 1]);
259 for (size_t i = count; i--;) {
260 if (!iscombining(str[i]))
261 return count - (!is_low_surrogate(str[i]) || i == 0 || !is_high_surrogate(str[i - 1]) ? i : i - 1);
262 }
263 return count;
264 }
265
272 inline size_t glyphrlen(_In_reads_or_z_opt_(count) const utf32_t* str, _In_ size_t count)
273 {
274 stdex_assert(count && str && str[count - 1]);
275 for (size_t i = count; i--;) {
276 if (!iscombining(str[i]))
277 return count - (i == 0 ? i : i - 1);
278 }
279 return count;
280 }
281
289 template <class T>
290 T tolower(_In_ T chr)
291 {
292 return isupper(chr) ? chr | 0x20 : chr;
293 }
294
302 template <class T>
303 T toupper(_In_ T chr)
304 {
305 return islower(chr) ? chr | ~0x20 : chr;
306 }
307
315 template <class T>
316 size_t strlen(_In_z_ const T* str)
317 {
318 stdex_assert(str);
319 size_t i;
320 for (i = 0; str[i]; ++i);
321 return i;
322 }
323
332 template <class T>
333 size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
334 {
335 stdex_assert(str || !count);
336 size_t i;
337 for (i = 0; i < count && str[i]; ++i);
338 return i;
339 }
340
348 template <class T, size_t N>
349 size_t strnlen(_In_ const T (&str)[N])
350 {
351 return strnlen(str, N);
352 }
353
354 constexpr auto npos{ static_cast<size_t>(-1) };
355
364 template <class T>
365 size_t strchr(_In_z_ const T* str, _In_ T chr)
366 {
367 stdex_assert(str);
368 for (size_t i = 0; str[i]; ++i)
369 if (str[i] == chr) return i;
370 return npos;
371 }
372
382 template <class T>
383 size_t strnchr(
384 _In_reads_or_z_opt_(count) const T* str,
385 _In_ size_t count,
386 _In_ T chr)
387 {
388 stdex_assert(str || !count);
389 for (size_t i = 0; i < count && str[i]; ++i)
390 if (str[i] == chr) return i;
391 return npos;
392 }
393
402 template <class T, size_t N>
403 size_t strnchr(
404 _In_ const T (&str)[N],
405 _In_ T chr)
406 {
407 return strnchr(str, N, chr);
408 }
409
418 template <class T>
419 size_t strrchr(
420 _In_z_ const T* str,
421 _In_ T chr)
422 {
423 stdex_assert(str);
424 size_t z = npos;
425 for (size_t i = 0; str[i]; ++i)
426 if (str[i] == chr) z = i;
427 return z;
428 }
429
439 template <class T>
440 size_t strrnchr(
441 _In_reads_or_z_opt_(count) const T* str,
442 _In_ size_t count,
443 _In_ T chr)
444 {
445 stdex_assert(str || !count);
446 size_t z = npos;
447 for (size_t i = 0; i < count && str[i]; ++i)
448 if (str[i] == chr) z = i;
449 return z;
450 }
451
460 template <class T, size_t N>
461 size_t strrnchr(
462 _In_ const T (&str)[N],
463 _In_ T chr)
464 {
465 return strrnchr(str, N, chr);
466 }
467
476 template <class T>
477 size_t strichr(
478 _In_z_ const T* str,
479 _In_ T chr)
480 {
481 stdex_assert(str);
482 chr = tolower(chr);
483 for (size_t i = 0; str[i]; ++i)
484 if (tolower(str[i]) == chr) return i;
485 return npos;
486 }
487
497 template <class T>
498 size_t strichr(
499 _In_z_ const T* str,
500 _In_ T chr,
501 _In_ const std::locale& locale)
502 {
503 stdex_assert(str);
504 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
505 chr = ctype.tolower(chr);
506 for (size_t i = 0; str[i]; ++i)
507 if (ctype.tolower(str[i]) == chr) return i;
508 return npos;
509 }
510
520 template <class T>
521 size_t strnichr(
522 _In_reads_or_z_opt_(count) const T* str,
523 _In_ size_t count,
524 _In_ T chr)
525 {
526 stdex_assert(str || !count);
527 chr = tolower(chr);
528 for (size_t i = 0; i < count && str[i]; ++i)
529 if (tolower(str[i]) == chr) return i;
530 return npos;
531 }
532
543 template <class T>
544 size_t strnichr(
545 _In_reads_or_z_opt_(count) const T* str,
546 _In_ size_t count,
547 _In_ T chr,
548 _In_ const std::locale& locale)
549 {
550 stdex_assert(str || !count);
551 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
552 chr = ctype.tolower(chr);
553 for (size_t i = 0; i < count && str[i]; ++i)
554 if (ctype.tolower(str[i]) == chr) return i;
555 return npos;
556 }
557
566 template <class T, size_t N>
567 size_t strnichr(
568 _In_ const T (&str)[N],
569 _In_ T chr)
570 {
571 return strnichr(str, N, chr);
572 }
573
583 template <class T, size_t N>
584 size_t strnichr(
585 _In_ const T (&str)[N],
586 _In_ T chr,
587 _In_ const std::locale& locale)
588 {
589 return strnichr(str, N, chr, locale);
590 }
591
600 template <class T>
601 size_t strrichr(
602 _In_z_ const T* str,
603 _In_ T chr)
604 {
605 stdex_assert(str);
606 chr = tolower(chr);
607 size_t z = npos;
608 for (size_t i = 0; str[i]; ++i)
609 if (tolower(str[i]) == chr) z = i;
610 return z;
611 }
612
622 template <class T>
623 size_t strrichr(
624 _In_reads_or_z_opt_(count) const T* str,
625 _In_ T chr,
626 _In_ const std::locale& locale)
627 {
628 stdex_assert(str);
629 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
630 chr = ctype.tolower(chr);
631 size_t z = npos;
632 for (size_t i = 0; str[i]; ++i)
633 if (ctype.tolower(str[i]) == chr) z = i;
634 return z;
635 }
636
646 template <class T>
647 size_t strrnichr(
648 _In_reads_or_z_opt_(count) const T* str,
649 _In_ size_t count,
650 _In_ T chr)
651 {
652 stdex_assert(str || !count);
653 chr = tolower(chr);
654 size_t z = npos;
655 for (size_t i = 0; i < count && str[i]; ++i)
656 if (tolower(str[i]) == chr) z = i;
657 return z;
658 }
659
670 template <class T>
671 size_t strrnichr(
672 _In_reads_or_z_opt_(count) const T* str,
673 _In_ size_t count,
674 _In_ T chr,
675 _In_ const std::locale& locale)
676 {
677 stdex_assert(str || !count);
678 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
679 chr = ctype.tolower(chr);
680 size_t z = npos;
681 for (size_t i = 0; i < count && str[i]; ++i)
682 if (ctype.tolower(str[i]) == chr) z = i;
683 return z;
684 }
685
694 template <class T, size_t N>
695 size_t strrnichr(
696 _In_ const T (&str)[N],
697 _In_ T chr)
698 {
699 return strrnichr(str, N, chr);
700 }
701
711 template <class T, size_t N>
712 size_t strrnichr(
713 _In_ const T (&str)[N],
714 _In_ T chr,
715 _In_ const std::locale& locale)
716 {
717 return strrnichr(str, N, chr, locale);
718 }
719
727 //template <class T>
728 //bool isblank(_In_z_ const T* str)
729 //{
730 // stdex_assert(str);
731 // for (size_t i = 0; str[i]; ++i)
732 // if (!isspace(str[i]))
733 // return false;
734 // return true;
735 //}
736
745 //template <class T>
746 //bool isblank(
747 // _In_z_ const T* str,
748 // _In_ const std::locale& locale)
749 //{
750 // stdex_assert(str);
751 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
752 // for (size_t i = 0; str[i]; ++i)
753 // if (!ctype.is(ctype.space, str[i]))
754 // return false;
755 // return true;
756 //}
757
766 template <class T>
767 bool isblank(
768 _In_reads_or_z_opt_(count) const T* str,
769 _In_ size_t count)
770 {
771 stdex_assert(str || !count);
772 for (size_t i = 0; i < count && str[i]; ++i)
773 if (!isspace(str[i]))
774 return false;
775 return true;
776 }
777
787 template <class T>
788 bool isblank(
789 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
790 _In_ const std::locale& locale)
791 {
792 stdex_assert(str || !count);
793 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
794 for (size_t i = 0; i < count && str[i]; ++i)
795 if (!ctype.is(ctype.space, str[i]))
796 return false;
797 return true;
798 }
799
807 template <class T, size_t N>
808 bool isblank(_In_ const T (&str)[N])
809 {
810 return isblank(str, N);
811 }
812
821 template <class T, size_t N>
822 bool isblank(
823 _In_ const T (&str)[N],
824 _In_ const std::locale& locale)
825 {
826 return isblank(str, N, locale);
827 }
828
829 // ///
830 // /// Checks if string contains all-ASCII characters
831 // ///
832 // /// \param[in] str String
833 // ///
834 // /// \return `true` if all characters are ASCII or `false` when any non-ASCII character is found in string.
835 // ///
836 // template <class T>
837 // bool is7bit(_In_z_ const T* str)
838 // {
839 // stdex_assert(str);
840 // for (size_t i = 0; str[i]; i++)
841 // if (!is7bit(str[i]))
842 // return false;
843 // return true;
844 // }
845
854 template <class T>
855 bool is7bit(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
856 {
857 stdex_assert(str || !count);
858 for (size_t i = 0; i < count && str[i]; i++)
859 if (!is7bit(str[i]))
860 return false;
861 return true;
862 }
863
871 template <class T, size_t N>
872 bool is7bit(_In_ const T (&str)[N])
873 {
874 return is7bit(str, N);
875 }
876
885 template <class T1, class T2>
886 int strcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
887 {
888 stdex_assert(str1);
889 stdex_assert(str2);
890 size_t i; T1 a; T2 b;
891 for (i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
892 if (a > b) return +1;
893 if (a < b) return -1;
894 }
895 if (str1[i]) return +1;
896 if (str2[i]) return -1;
897 return 0;
898 }
899
909 template <class T1, class T2>
910 int strncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
911 {
912 stdex_assert(str1 || !count);
913 stdex_assert(str2 || !count);
914 size_t i; T1 a; T2 b;
915 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
916 if (a > b) return +1;
917 if (a < b) return -1;
918 }
919 if (i < count && str1[i]) return +1;
920 if (i < count && str2[i]) return -1;
921 return 0;
922 }
923
934 template <class T1, class T2>
935 int strncmp(
936 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
937 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
938 {
939 stdex_assert(str1 || !count1);
940 stdex_assert(str2 || !count2);
941 size_t i;
942 for (i = 0; i < count1 && i < count2; ++i) {
943 auto a = str1[i];
944 auto b = str2[i];
945 if (!a && !b) return 0;
946 if (a > b) return +1;
947 if (a < b) return -1;
948 }
949 if (i < count1 && str1[i]) return +1;
950 if (i < count2 && str2[i]) return -1;
951 return 0;
952 }
953
962 template <class T1, size_t N1, class T2, size_t N2>
963 int strncmp(
964 _In_ const T1 (&str1)[N1],
965 _In_ const T2 (&str2)[N2])
966 {
967 return strncmp(str1, N1, str2, N2);
968 }
969
980 inline int strncmp(
981 _In_reads_or_z_opt_(count1) const utf32_t* str1, _In_ size_t count1,
982 _In_reads_or_z_opt_(count2) const utf16_t* str2, _In_ size_t count2)
983 {
984 stdex_assert(str1 || !count1);
985 stdex_assert(str2 || !count2);
986 size_t i, j, j_next; utf32_t a, b;
987 for (i = 0, j = 0; i < count1 && j < count2; ++i, j = j_next) {
988 a = str1[i];
989 if (!a)
990 break;
991 if (j + 1 >= count2 || !is_surrogate_pair(&str2[j])) {
992 b = static_cast<utf32_t>(str2[j]);
993 j_next = j + 1;
994 }
995 else {
996 b = surrogate_pair_to_ucs4(&str2[j]);
997 j_next = j + 2;
998 }
999 if (!b)
1000 break;
1001 if (a > b) return +1;
1002 if (a < b) return -1;
1003 }
1004 if (i < count1 && str1[i]) return +1;
1005 if (j < count2 && str2[j]) return -1;
1006 return 0;
1007 }
1008
1017 template <size_t N1, size_t N2>
1018 int strncmp(
1019 _In_ const utf32_t (&str1)[N1],
1020 _In_ const utf16_t (&str2)[N2])
1021 {
1022 return strncmp(str1, N1, str2, N2);
1023 }
1024
1033 template <class T1, class T2>
1034 int strrcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
1035 {
1036 size_t
1037 i = strlen(str1),
1038 j = strlen(str2);
1039 stdex_assert(str1 || !i);
1040 stdex_assert(str2 || !j);
1041 size_t k; T1 a; T2 b;
1042 for (k = 1; i && j; k++) {
1043 i--; j--;
1044 if ((a = str1[i]) > (b = str2[j])) return +1;
1045 if (a < b) return -1;
1046 }
1047 if (i && !j) return +1;
1048 if (!i && j) return -1;
1049 return 0;
1050 }
1051
1061 template <class T1, class T2>
1062 int strrncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
1063 {
1064 size_t
1065 i = strnlen(str1, count),
1066 j = strnlen(str2, count);
1067 stdex_assert(str1 || !i);
1068 stdex_assert(str2 || !j);
1069 size_t k; T1 a; T2 b;
1070 for (k = 1; i && j; k++) {
1071 i--; j--;
1072 if ((a = str1[i]) > (b = str2[j])) return +1;
1073 if (a < b) return -1;
1074 }
1075 if (i && !j) return +1;
1076 if (!i && j) return -1;
1077 return 0;
1078 }
1079
1090 template <class T1, class T2>
1091 int strrncmp(
1092 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1093 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1094 {
1095 size_t
1096 i = strnlen(str1, count1),
1097 j = strnlen(str2, count2);
1098 stdex_assert(str1 || !i);
1099 stdex_assert(str2 || !j);
1100 size_t k; T1 a; T2 b;
1101 for (k = 1; i && j; k++) {
1102 i--; j--;
1103 if ((a = str1[i]) > (b = str2[j])) return +1;
1104 if (a < b) return -1;
1105 }
1106 if (i && !j) return +1;
1107 if (!i && j) return -1;
1108 return 0;
1109 }
1110
1119 template <class T1, size_t N1, class T2, size_t N2>
1120 int strrncmp(
1121 _In_ const T1 (&str1)[N1],
1122 _In_ const T2 (&str2)[N2])
1123 {
1124 return strrncmp(str1, N1, str2, N2);
1125 }
1126
1135 template <class T1, class T2>
1136 int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
1137 {
1138 stdex_assert(str1);
1139 stdex_assert(str2);
1140 size_t i;
1141 for (i = 0; ; ++i) {
1142 auto a = tolower(str1[i]);
1143 auto b = tolower(str2[i]);
1144 if (!a && !b) return 0;
1145 if (a > b) return +1;
1146 if (a < b) return -1;
1147 }
1148 }
1149
1159 template <class T1, class T2>
1160 int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
1161 {
1162 stdex_assert(str1);
1163 stdex_assert(str2);
1164 size_t i;
1165 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1166 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1167 for (i = 0;; ++i) {
1168 auto a = ctype1.tolower(str1[i]);
1169 auto b = ctype2.tolower(str2[i]);
1170 if (!a && !b) return 0;
1171 if (a > b) return +1;
1172 if (a < b) return -1;
1173 }
1174 }
1175
1185 template <class T1, class T2>
1186 int strnicmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
1187 {
1188 stdex_assert(str1 || !count);
1189 stdex_assert(str2 || !count);
1190 size_t i;
1191 for (i = 0; i < count; ++i) {
1192 auto a = tolower(str1[i]);
1193 auto b = tolower(str2[i]);
1194 if (!a && !b) return 0;
1195 if (a > b) return +1;
1196 if (a < b) return -1;
1197 }
1198 if (i < count && str1[i]) return +1;
1199 if (i < count && str2[i]) return -1;
1200 return 0;
1201 }
1202
1213 template <class T1, class T2>
1214 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)
1215 {
1216 stdex_assert(str1 || !count);
1217 stdex_assert(str2 || !count);
1218 size_t i;
1219 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1220 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1221 for (i = 0; i < count; ++i) {
1222 auto a = ctype1.tolower(str1[i]);
1223 auto b = ctype2.tolower(str2[i]);
1224 if (!a && !b) return 0;
1225 if (a > b) return +1;
1226 if (a < b) return -1;
1227 }
1228 if (i < count && str1[i]) return +1;
1229 if (i < count && str2[i]) return -1;
1230 return 0;
1231 }
1232
1243 template <class T1, class T2>
1244 int strnicmp(
1245 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1246 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1247 {
1248 stdex_assert(str1 || !count1);
1249 stdex_assert(str2 || !count2);
1250 size_t i;
1251 for (i = 0; i < count1 && i < count2; ++i) {
1252 auto a = tolower(str1[i]);
1253 auto b = tolower(str2[i]);
1254 if (!a && !b) return 0;
1255 if (a > b) return +1;
1256 if (a < b) return -1;
1257 }
1258 if (i < count1 && str1[i]) return +1;
1259 if (i < count2 && str2[i]) return -1;
1260 return 0;
1261 }
1262
1274 template <class T1, class T2>
1275 int strnicmp(
1276 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1277 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
1278 _In_ const std::locale& locale)
1279 {
1280 stdex_assert(str1 || !count1);
1281 stdex_assert(str2 || !count2);
1282 size_t i;
1283 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1284 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1285 for (i = 0; i < count1 && i < count2; ++i) {
1286 auto a = ctype1.tolower(str1[i]);
1287 auto b = ctype2.tolower(str2[i]);
1288 if (!a && !b) return 0;
1289 if (a > b) return +1;
1290 if (a < b) return -1;
1291 }
1292 if (i < count1 && str1[i]) return +1;
1293 if (i < count2 && str2[i]) return -1;
1294 return 0;
1295 }
1296
1305 template <class T1, size_t N1, class T2, size_t N2>
1306 int strnicmp(
1307 _In_ const T1 (&str1)[N1],
1308 _In_ const T2 (&str2)[N2])
1309 {
1310 strnicmp(str1, N1, str2, N2);
1311 }
1312
1322 template <class T1, size_t N1, class T2, size_t N2>
1323 int strnicmp(
1324 _In_ const T1 (&str1)[N1],
1325 _In_ const T2 (&str2)[N2],
1326 _In_ const std::locale& locale)
1327 {
1328 strnicmp(str1, N1, str2, N2, locale);
1329 }
1330
1340 template <class T>
1341 int strcoll(
1342 _In_z_ const T* str1,
1343 _In_z_ const T* str2,
1344 _In_ const std::locale& locale)
1345 {
1346 stdex_assert(str1);
1347 stdex_assert(str2);
1348 auto& collate = std::use_facet<std::collate<T>>(locale);
1349 return collate.compare(str1, str1 + strlen(str1), str2, str2 + strlen(str2));
1350 }
1351
1363 template <class T>
1364 int strncoll(
1365 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
1366 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
1367 _In_ const std::locale& locale)
1368 {
1369 stdex_assert(str1 || !count1);
1370 stdex_assert(str2 || !count2);
1371 auto& collate = std::use_facet<std::collate<T>>(locale);
1372 return collate.compare(str1, str1 + count1, str2, str2 + count2);
1373 }
1374
1384 template <class T, size_t N1, size_t N2>
1385 int strncoll(
1386 _In_ const T (&str1)[N1],
1387 _In_ const T (&str2)[N2],
1388 _In_ const std::locale& locale)
1389 {
1390 return strncoll(str1, N1, str2, N2, locale);
1391 }
1392
1401 template <class T1, class T2>
1402 size_t strstr(
1403 _In_z_ const T1* str,
1404 _In_z_ const T2* sample)
1405 {
1406 stdex_assert(str);
1407 stdex_assert(sample);
1408 for (size_t offset = 0;; ++offset) {
1409 for (size_t i = offset, j = 0;; ++i, ++j) {
1410 if (!sample[j])
1411 return offset;
1412 if (!str[i])
1413 return npos;
1414 if (str[i] != sample[j])
1415 break;
1416 }
1417 }
1418 }
1419
1429 template <class T1, class T2>
1430 size_t strnstr(
1431 _In_reads_or_z_opt_(count) const T1* str, _In_ size_t count,
1432 _In_z_ const T2* sample)
1433 {
1434 stdex_assert(str || !count);
1435 stdex_assert(sample);
1436 for (size_t offset = 0;; ++offset) {
1437 for (size_t i = offset, j = 0;; ++i, ++j) {
1438 if (!sample[j])
1439 return offset;
1440 if (i >= count || !str[i])
1441 return npos;
1442 if (str[i] != sample[j])
1443 break;
1444 }
1445 }
1446 }
1447
1456 template <class T1, size_t N1, class T2>
1457 size_t strnstr(
1458 _In_ const T1 (&str)[N1],
1459 _In_z_ const T2* sample)
1460 {
1461 return strnstr(str, N1, sample);
1462 }
1463
1472 template <class T1, class T2>
1473 size_t stristr(
1474 _In_z_ const T1* str,
1475 _In_z_ const T2* sample)
1476 {
1477 stdex_assert(str);
1478 stdex_assert(sample);
1479 for (size_t offset = 0;; ++offset) {
1480 for (size_t i = offset, j = 0;; ++i, ++j) {
1481 if (!sample[j])
1482 return offset;
1483 if (!str[i])
1484 return npos;
1485 if (tolower(str[i]) != tolower(sample[j]))
1486 break;
1487 }
1488 }
1489 }
1490
1500 template <class T1, class T2>
1501 size_t stristr(
1502 _In_z_ const T1* str,
1503 _In_z_ const T2* sample,
1504 _In_ const std::locale& locale)
1505 {
1506 stdex_assert(str);
1507 stdex_assert(sample);
1508 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1509 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1510 for (size_t offset = 0;; ++offset) {
1511 for (size_t i = offset, j = 0;; ++i, ++j) {
1512 if (!sample[j])
1513 return offset;
1514 if (!str[i])
1515 return npos;
1516 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1517 break;
1518 }
1519 }
1520 }
1521
1531 template <class T1, class T2>
1532 size_t strnistr(
1533 _In_reads_or_z_opt_(count) const T1* str,
1534 _In_ size_t count,
1535 _In_z_ const T2* sample)
1536 {
1537 stdex_assert(str || !count);
1538 stdex_assert(sample);
1539 for (size_t offset = 0;; ++offset) {
1540 for (size_t i = offset, j = 0;; ++i, ++j) {
1541 if (!sample[j])
1542 return offset;
1543 if (i >= count || !str[i])
1544 return npos;
1545 if (tolower(str[i]) != tolower(sample[j]))
1546 break;
1547 }
1548 }
1549 }
1550
1561 template <class T1, class T2>
1562 size_t strnistr(
1563 _In_reads_or_z_opt_(count) const T1* str,
1564 _In_ size_t count,
1565 _In_z_ const T2* sample,
1566 _In_ const std::locale& locale)
1567 {
1568 stdex_assert(str || !count);
1569 stdex_assert(sample);
1570 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1571 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1572 for (size_t offset = 0;; ++offset) {
1573 for (size_t i = offset, j = 0;; ++i, ++j) {
1574 if (!sample[j])
1575 return offset;
1576 if (i >= count || !str[i])
1577 return npos;
1578 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1579 break;
1580 }
1581 }
1582 }
1583
1592 template <class T1, size_t N1, class T2>
1593 size_t strnistr(
1594 _In_ const T1 (&str)[N1],
1595 _In_z_ const T2* sample)
1596 {
1597 return strnistr(str, N1, sample);
1598 }
1599
1609 template <class T1, size_t N1, class T2>
1610 size_t strnistr(
1611 _In_ const T1 (&str)[N1],
1612 _In_z_ const T2* sample,
1613 _In_ const std::locale& locale)
1614 {
1615 return strnistr(str, N1, sample, locale);
1616 }
1617
1626 template <class T1, class T2>
1627 size_t strcpy(
1628 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
1629 _In_z_ const T2* src)
1630 {
1631 stdex_assert(dst);
1632 stdex_assert(src);
1633 for (size_t i = 0; ; ++i) {
1634 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1635 return i;
1636 }
1637 }
1638
1647 inline size_t strcpy(
1648 _Out_writes_z_(_String_length_(src) + 1) utf32_t* dst,
1649 _In_z_ const utf16_t* src)
1650 {
1651 stdex_assert(dst);
1652 stdex_assert(src);
1653 for (size_t j = 0, i = 0; ; ++j, ++i) {
1654 // strcpy has no knowledge, how big the dst buffer is, but we know, we won't be writing more than strlen(src) + 1 characters into it.
1655 // Code Analysis somehow doesn't work this out from the code and the dst SAL above, and reports a false-positive warning.
1656#pragma warning(suppress: 6386)
1657 if ((dst[j] = (is_surrogate_pair(&src[i]) ? surrogate_pair_to_ucs4(&src[i++]) : static_cast<utf32_t>(src[i]))) == 0)
1658 return j;
1659 }
1660 }
1661
1671 template <class T1, class T2>
1672 size_t strncpy(
1673 _Out_writes_(count) _Post_maybez_ T1* dst,
1674 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1675 {
1676 stdex_assert(dst || !count);
1677 stdex_assert(src || !count);
1678 for (size_t i = 0; ; ++i) {
1679 if (i >= count)
1680 return i;
1681 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1682 return i;
1683 }
1684 }
1685
1696 template <class T1, class T2>
1697 size_t strncpy(
1698 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1699 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1700 {
1701 stdex_assert(dst || !count_dst);
1702 stdex_assert(src || !count_src);
1703 for (size_t i = 0; ; ++i)
1704 {
1705 if (i >= count_dst)
1706 return i;
1707 if (i >= count_src) {
1708 dst[i] = 0;
1709 return i;
1710 }
1711 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1712 return i;
1713 }
1714 }
1715
1726 inline size_t strncpy(
1727 _Out_writes_(count_dst) _Post_maybez_ utf32_t* dst, _In_ size_t count_dst,
1728 _In_reads_or_z_opt_(count_src) const utf16_t* src, _In_ size_t count_src)
1729 {
1730 stdex_assert(dst || !count_dst);
1731 stdex_assert(src || !count_src);
1732 for (size_t j = 0, i = 0; ; ++j, ++i)
1733 {
1734 if (j >= count_dst)
1735 return j;
1736 if (i >= count_src) {
1737 dst[j] = 0;
1738 return j;
1739 }
1740 if ((dst[j] = (i + 1 < count_src && is_surrogate_pair(&src[i]) ? surrogate_pair_to_ucs4(&src[i++]) : static_cast<utf32_t>(src[i]))) == 0)
1741 return j;
1742 }
1743 }
1744
1753 template <class T1, size_t N1, class T2, size_t N2>
1754 size_t strncpy(
1755 _Out_ _Post_maybez_ T1 (&dst)[N1],
1756 _In_ const T2 (&src)[N2])
1757 {
1758 return strncpy(dst, N1, src, N2);
1759 }
1760
1769 template <class T1, class T2>
1770 size_t strcat(
1771 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
1772 _In_z_ const T2* src)
1773 {
1774 stdex_assert(dst);
1775 stdex_assert(src);
1776 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1777 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1778 return j;
1779 }
1780 }
1781
1791 template <class T1, class T2>
1792 size_t strncat(
1793 _Inout_z_ T1* dst,
1794 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1795 {
1796 stdex_assert(dst || !count);
1797 stdex_assert(src || !count);
1798 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1799 if (i >= count)
1800 return j;
1801 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1802 return j;
1803 }
1804 }
1805
1816 template <class T1, class T2>
1817 size_t strncat(
1818 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1819 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1820 {
1821 stdex_assert(dst || !count_dst);
1822 stdex_assert(src || !count_src);
1823 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
1824 {
1825 if (j >= count_dst)
1826 return j;
1827 if (i >= count_src) {
1828 dst[j] = 0;
1829 return j;
1830 }
1831 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1832 return j;
1833 }
1834 }
1835
1846 template <class T>
1847 _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
1848 {
1849 if (!str) _Unlikely_
1850 return nullptr;
1851 size_t count = strlen(str) + 1;
1852 T* dst = new T[count];
1853 strncpy(dst, count, str, SIZE_MAX);
1854 return dst;
1855 }
1856
1868 template <class T>
1869 _Ret_z_ T* strndup(
1870 _In_reads_or_z_opt_(count) const T* str,
1871 _In_ size_t count)
1872 {
1873 T* dst = new T[count];
1874 strncpy(dst, count, str, SIZE_MAX);
1875 return dst;
1876 }
1877
1888 template <class T, size_t N>
1889 _Check_return_ _Ret_maybenull_z_ T* strndup(_In_ const T (&str)[N])
1890 {
1891 return strndup(str, N);
1892 }
1893
1903 template <class T>
1904 size_t crlf2nl(_Out_writes_z_(_String_length_(src) + 1) T* dst, _In_z_ const T* src)
1905 {
1906 stdex_assert(dst);
1907 stdex_assert(src);
1908 size_t i, j;
1909 for (i = j = 0; src[j];) {
1910 if (src[j] != '\r' || src[j + 1] != '\n')
1911 dst[i++] = src[j++];
1912 else {
1913 dst[i++] = '\n';
1914 j += 2;
1915 }
1916 }
1917 dst[i] = 0;
1918 return i;
1919 }
1920
1927 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1928 void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& dst, _In_z_ const T* src)
1929 {
1930 stdex_assert(src);
1931 stdex_assert(src != dst.data());
1932 dst.clear();
1933 dst.reserve(strlen(src));
1934 for (size_t j = 0; src[j];) {
1935 if (src[j] != '\r' || src[j + 1] != '\n')
1936 dst += src[j++];
1937 else {
1938 dst += '\n';
1939 j += 2;
1940 }
1941 }
1942 }
1943
1949 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1950 void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& str)
1951 {
1952 size_t i, j, n;
1953 for (i = j = 0, n = str.size(); j < n;) {
1954 if (str[j] != '\r' || str[j + 1] != '\n')
1955 str[i++] = str[j++];
1956 else {
1957 str[i++] = '\n';
1958 j += 2;
1959 }
1960 }
1961 str.resize(i);
1962 }
1963
1965 template <class T, class T_bin>
1966 T_bin strtoint(
1967 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1968 _Out_opt_ size_t* end,
1969 _In_ int radix,
1970 _Out_ uint8_t& flags)
1971 {
1972 stdex_assert(str || !count);
1973 stdex_assert(radix == 0 || 2 <= radix && radix <= 36);
1974
1975 size_t i = 0;
1976 T_bin value = 0, digit,
1977 max_ui = (T_bin)-1,
1978 max_ui_pre1, max_ui_pre2;
1979
1980 flags = 0;
1981
1982 // Skip leading spaces.
1983 for (;; ++i) {
1984 if (i >= count || !str[i]) goto error;
1985 if (!isspace(str[i])) break;
1986 }
1987
1988 // Read the sign.
1989 if (str[i] == '+') {
1990 flags &= ~0x01;
1991 ++i;
1992 if (i >= count || !str[i]) goto error;
1993 }
1994 else if (str[i] == '-') {
1995 flags |= 0x01;
1996 ++i;
1997 if (i >= count || !str[i]) goto error;
1998 }
1999
2000 if (radix == 16) {
2001 // On hexadecimal, allow leading 0x.
2002 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
2003 i += 2;
2004 if (i >= count || !str[i]) goto error;
2005 }
2006 }
2007 else if (!radix) {
2008 // Autodetect radix.
2009 if (str[i] == '0') {
2010 ++i;
2011 if (i >= count || !str[i]) goto error;
2012 if (str[i] == 'x' || str[i] == 'X') {
2013 radix = 16;
2014 ++i;
2015 if (i >= count || !str[i]) goto error;
2016 }
2017 else
2018 radix = 8;
2019 }
2020 else
2021 radix = 10;
2022 }
2023
2024 // We have the radix.
2025 max_ui_pre1 = max_ui / (T_bin)radix;
2026 max_ui_pre2 = max_ui % (T_bin)radix;
2027 for (;;) {
2028 if ('0' <= str[i] && str[i] <= '9')
2029 digit = (T_bin)str[i] - '0';
2030 else if ('A' <= str[i] && str[i] <= 'Z')
2031 digit = (T_bin)str[i] - 'A' + '\x0a';
2032 else if ('a' <= str[i] && str[i] <= 'z')
2033 digit = (T_bin)str[i] - 'a' + '\x0a';
2034 else
2035 goto error;
2036 if (digit >= (T_bin)radix)
2037 goto error;
2038
2039 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
2040 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
2041 value = value * (T_bin)radix + digit;
2042 else {
2043 // Overflow!
2044 flags |= 0x02;
2045 }
2046
2047 ++i;
2048 if (i >= count || !str[i])
2049 goto error;
2050 }
2051
2052 error:
2053 if (end) *end = i;
2054 return value;
2055 }
2057
2068 template <class T, class T_bin>
2069 T_bin strtoint(
2070 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2071 _Out_opt_ size_t* end,
2072 _In_ int radix)
2073 {
2074 uint8_t flags;
2075 T_bin value;
2076
2077 switch (sizeof(T_bin)) {
2078 case 1:
2079 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
2080 if ((flags & 0x01) && (value & 0x80)) {
2081 // Sign bit is 1 => overflow.
2082 flags |= 0x02;
2083 }
2084 return (flags & 0x02) ?
2085 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
2086 (flags & 0x01) ? -value : value;
2087
2088 case 2:
2089 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
2090 if ((flags & 0x01) && (value & 0x8000)) {
2091 // Sign bit is 1 => overflow.
2092 flags |= 0x02;
2093 }
2094 return (flags & 0x02) ?
2095 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
2096 (flags & 0x01) ? -value : value;
2097
2098 case 4:
2099 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
2100 if ((flags & 0x01) && (value & 0x80000000)) {
2101 // Sign bit is 1 => overflow.
2102 flags |= 0x02;
2103 }
2104 return (flags & 0x02) ?
2105 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
2106 (flags & 0x01) ? -value : value;
2107
2108 case 8:
2109 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
2110 if ((flags & 0x01) && (value & 0x8000000000000000)) {
2111 // Sign bit is 1 => overflow.
2112 flags |= 0x02;
2113 }
2114 return (flags & 0x02) ?
2115 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
2116 (flags & 0x01) ? -value : value;
2117
2118 default:
2119 throw std::invalid_argument("Unsupported bit length");
2120 }
2121 }
2122
2132 template <class T, size_t N, class T_bin>
2133 T_bin strtoint(
2134 _In_ const T (&str)[N],
2135 _Out_opt_ size_t* end,
2136 _In_ int radix)
2137 {
2138 return strtoint<T, T_bin>(str, N, end, radix);
2139 }
2140
2151 template <class T, class T_bin>
2152 T_bin strtouint(
2153 _In_reads_or_z_opt_(count) const T* str,
2154 _In_ size_t count,
2155 _Out_opt_ size_t* end,
2156 _In_ int radix)
2157 {
2158 uint8_t flags;
2159 T_bin value;
2160
2161 switch (sizeof(T_bin)) {
2162 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
2163 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
2164 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
2165 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
2166 default: throw std::invalid_argument("Unsupported bit length");
2167 }
2168
2169 return (flags & 0x02) ?
2170 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
2171 (flags & 0x01) ? ~value : value;
2172 }
2173
2183 template <class T, size_t N, class T_bin>
2184 T_bin strtouint(
2185 _In_ const T (&str)[N],
2186 _Out_opt_ size_t* end,
2187 _In_ int radix)
2188 {
2189 return strtouint<T, T_bin>(str, N, end, radix);
2190 }
2191
2202 template <class T>
2203 int8_t strto8(
2204 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2205 _Out_opt_ size_t* end,
2206 _In_ int radix)
2207 {
2208 return strtoint<T, int8_t>(str, count, end, radix);
2209 }
2210
2220 template <class T, size_t N>
2221 int8_t strto8(
2222 _In_ const T (&str)[N],
2223 _Out_opt_ size_t* end,
2224 _In_ int radix)
2225 {
2226 return strto8<T>(str, N, end, radix);
2227 }
2228
2239 template <class T>
2240 int16_t strto16(
2241 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2242 _Out_opt_ size_t* end,
2243 _In_ int radix)
2244 {
2245 return strtoint<T, int16_t>(str, count, end, radix);
2246 }
2247
2257 template <class T, size_t N>
2258 int16_t strto16(
2259 _In_ const T (&str)[N],
2260 _Out_opt_ size_t* end,
2261 _In_ int radix)
2262 {
2263 return strto16<T>(str, N, end, radix);
2264 }
2265
2276 template <class T>
2277 int32_t strto32(
2278 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2279 _Out_opt_ size_t* end,
2280 _In_ int radix)
2281 {
2282 return strtoint<T, int32_t>(str, count, end, radix);
2283 }
2284
2294 template <class T, size_t N>
2295 int32_t strto32(
2296 _In_ const T (&str)[N],
2297 _Out_opt_ size_t* end,
2298 _In_ int radix)
2299 {
2300 return strto32<T>(str, N, end, radix);
2301 }
2302
2313 template <class T>
2314 int64_t strto64(
2315 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2316 _Out_opt_ size_t* end,
2317 _In_ int radix)
2318 {
2319 return strtoint<T, int64_t>(str, count, end, radix);
2320 }
2321
2331 template <class T, size_t N>
2332 int64_t strto64(
2333 _In_ const T (&str)[N],
2334 _Out_opt_ size_t* end,
2335 _In_ int radix)
2336 {
2337 return strto64<T>(str, N, end, radix);
2338 }
2339
2351 template <class T>
2352 ptrdiff_t strtoi(
2353 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2354 _Out_opt_ size_t* end,
2355 _In_ int radix)
2356 {
2357#if defined(_WIN64) || defined(__LP64__)
2358 return static_cast<ptrdiff_t>(strto64(str, count, end, radix));
2359#else
2360 return static_cast<ptrdiff_t>(strto32(str, count, end, radix));
2361#endif
2362 }
2363
2374 template <class T, size_t N>
2375 ptrdiff_t strtoi(
2376 _In_ const T (&str)[N],
2377 _Out_opt_ size_t* end,
2378 _In_ int radix)
2379 {
2380 return strtoi<T>(str, N, end, radix);
2381 }
2382
2393 template <class T>
2394 uint8_t strtou8(
2395 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2396 _Out_opt_ size_t* end,
2397 _In_ int radix)
2398 {
2399 return strtouint<T, uint8_t>(str, count, end, radix);
2400 }
2401
2411 template <class T, size_t N>
2412 uint8_t strtou8(
2413 _In_ const T (&str)[N],
2414 _Out_opt_ size_t* end,
2415 _In_ int radix)
2416 {
2417 return strtou8(str, N, end, radix);
2418 }
2419
2430 template <class T>
2431 uint16_t strtou16(
2432 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2433 _Out_opt_ size_t* end,
2434 _In_ int radix)
2435 {
2436 return strtouint<T, uint16_t>(str, count, end, radix);
2437 }
2438
2448 template <class T, size_t N>
2449 uint16_t strtou16(
2450 _In_ const T (&str)[N],
2451 _Out_opt_ size_t* end,
2452 _In_ int radix)
2453 {
2454 return strtou16(str, N, end, radix);
2455 }
2456
2467 template <class T>
2468 uint32_t strtou32(
2469 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2470 _Out_opt_ size_t* end,
2471 _In_ int radix)
2472 {
2473 return strtouint<T, uint32_t>(str, count, end, radix);
2474 }
2475
2485 template <class T, size_t N>
2486 uint32_t strtou32(
2487 _In_ const T (&str)[N],
2488 _Out_opt_ size_t* end,
2489 _In_ int radix)
2490 {
2491 return strtou32(str, N, end, radix);
2492 }
2493
2504 template <class T>
2505 uint64_t strtou64(
2506 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2507 _Out_opt_ size_t* end,
2508 _In_ int radix)
2509 {
2510 return strtouint<T, uint64_t>(str, count, end, radix);
2511 }
2512
2522 template <class T, size_t N>
2523 uint64_t strtou64(
2524 _In_ const T (&str)[N],
2525 _Out_opt_ size_t* end,
2526 _In_ int radix)
2527 {
2528 return strtou64<T>(str, N, end, radix);
2529 }
2530
2542 template <class T>
2543 size_t strtoui(
2544 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2545 _Out_opt_ size_t* end,
2546 _In_ int radix)
2547 {
2548#if defined(_WIN64) || defined(__LP64__)
2549 return static_cast<size_t>(strtou64(str, count, end, radix));
2550#else
2551 return static_cast<size_t>(strtou32(str, count, end, radix));
2552#endif
2553 }
2554
2565 template <class T, size_t N>
2566 size_t strtoui(
2567 _In_ const T (&str)[N],
2568 _Out_opt_ size_t* end,
2569 _In_ int radix)
2570 {
2571 return strtoui<T>(str, N, end, radix);
2572 }
2573
2584 inline double strtod(
2585 _In_reads_or_z_opt_(count) const char* str, _In_ size_t count,
2586 _Out_opt_ size_t* end,
2587 _In_opt_ locale_t locale)
2588 {
2589 count = strnlen(str, count);
2590 stdex_assert(str || !count);
2591 std::string tmp(str, count);
2592 char* _end;
2593 double r;
2594#if _WIN32
2595 r = _strtod_l(tmp.c_str(), &_end, locale);
2596#else
2597 r = strtod_l(tmp.c_str(), &_end, locale);
2598#endif
2599 if (end) *end = (size_t)(_end - tmp.c_str());
2600 return r;
2601 }
2602
2613 inline double strtod(
2614 _In_reads_or_z_opt_(count) const wchar_t* str, _In_ size_t count,
2615 _Out_opt_ size_t* end,
2616 _In_opt_ locale_t locale)
2617 {
2618 count = strnlen(str, count);
2619 stdex_assert(str || !count);
2620 std::wstring tmp(str, count);
2621 wchar_t* _end;
2622 double r;
2623#if _WIN32
2624 r = _wcstod_l(tmp.c_str(), &_end, locale);
2625#else
2626 r = wcstod_l(tmp.c_str(), &_end, locale);
2627#endif
2628 if (end) *end = (size_t)(_end - tmp.c_str());
2629 return r;
2630 }
2631
2633 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)
2634 {
2635#ifdef _WIN32
2636#pragma warning(suppress: 4996)
2637 return _vsnprintf_l(str, capacity, format, locale, arg);
2638#else
2639 va_list arg_mutable;
2640 va_copy(arg_mutable, arg);
2641 return ::vsnprintf_l(str, capacity, locale, format, arg_mutable);
2642#endif
2643 }
2644
2645 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)
2646 {
2647#ifdef _WIN32
2648#pragma warning(suppress: 4996)
2649 return _vsnwprintf_l(str, capacity, format, locale, arg);
2650#else
2651 va_list arg_mutable;
2652 va_copy(arg_mutable, arg);
2653 return ::vswprintf_l(str, capacity, locale, format, arg_mutable);
2654#endif
2655 }
2657
2668 template<class T, class TR, class AX>
2669 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)
2670 {
2671 T buf[1024 / sizeof(T)];
2672
2673 // Try with stack buffer first.
2674 int count = vsnprintf(buf, _countof(buf), format, locale, arg);
2675 if (0 <= count && static_cast<size_t>(count) <= _countof(buf)) {
2676 // Copy from stack.
2677 str.append(buf, static_cast<size_t>(count));
2678 return static_cast<size_t>(count);
2679 }
2680#ifdef _WIN32
2681 if (count < 0) {
2682 switch (errno) {
2683 case 0:
2684 count = vsnprintf(NULL, 0, format, locale, arg);
2685 stdex_assert(count >= 0);
2686 break;
2687 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2688 case EILSEQ: throw std::runtime_error("encoding error");
2689 default: throw std::runtime_error("failed to format string");
2690 }
2691 }
2692 size_t offset = str.size();
2693 str.resize(offset + count);
2694 if (vsnprintf(&str[offset], count + 1, format, locale, arg) != count) _Unlikely_
2695 throw std::runtime_error("failed to format string");
2696#else
2697 size_t offset = str.size();
2698 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2699 switch (errno) {
2700 case EOVERFLOW:
2701 // Allocate on heap and retry.
2702 str.resize(offset + capacity);
2703 count = vsnprintf(&str[offset], capacity, format, locale, arg);
2704 if (0 <= count && static_cast<size_t>(count) <= capacity) {
2705 str.resize(offset + static_cast<size_t>(count));
2706 return static_cast<size_t>(count);
2707 }
2708 break;
2709 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2710 case EILSEQ: throw std::runtime_error("encoding error");
2711 default: throw std::runtime_error("failed to format string");
2712 }
2713 }
2714#endif
2715 return static_cast<size_t>(count);
2716 }
2717
2727 template<class T, class TR, class AX>
2728 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, ...)
2729 {
2730 va_list arg;
2731 va_start(arg, locale);
2732 size_t n = vappendf(str, format, locale, arg);
2733 va_end(arg);
2734 return n;
2735 }
2736
2745 template<class T, class TR, class AX>
2746 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)
2747 {
2748 str.clear();
2749 vappendf(str, format, locale, arg);
2750 }
2751
2759 template<class T, class TR, class AX>
2760 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, ...)
2761 {
2762 va_list arg;
2763 va_start(arg, locale);
2764 vsprintf(str, format, locale, arg);
2765 va_end(arg);
2766 }
2767
2777 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2778 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)
2779 {
2780 std::basic_string<T, TR, AX> str;
2781 vappendf(str, format, locale, arg);
2782 return str;
2783 }
2784
2793 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2794 std::basic_string<T, TR, AX> sprintf(_In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, ...)
2795 {
2796 va_list arg;
2797 va_start(arg, locale);
2798 auto str = vsprintf(format, locale, arg);
2799 va_end(arg);
2800 return str;
2801 }
2802
2804 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)
2805 {
2806#ifdef _WIN32
2807 return _strftime_l(str, capacity, format, time, locale);
2808#else
2809 return strftime_l(str, capacity, format, time, locale);
2810#endif
2811 }
2812
2813 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)
2814 {
2815#ifdef _WIN32
2816 return _wcsftime_l(str, capacity, format, time, locale);
2817#else
2818 return wcsftime_l(str, capacity, format, time, locale);
2819#endif
2820 }
2822
2831 template<class T, class TR, class AX>
2832 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)
2833 {
2834 T buf[1024 / sizeof(T)];
2835
2836 // Try with stack buffer first.
2837 size_t count = strftime(buf, _countof(buf), format, time, locale);
2838 if (count) {
2839 // Copy from stack.
2840 str.append(buf, count);
2841 return;
2842 }
2843 size_t offset = str.size();
2844 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2845 // Allocate on heap and retry.
2846 str.resize(offset + capacity);
2847 count = strftime(&str[offset], capacity + 1, format, time, locale);
2848 if (count) {
2849 str.resize(offset + count);
2850 return;
2851 }
2852 }
2853 }
2854
2863 template<class T, class TR, class AX>
2864 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)
2865 {
2866 str.clear();
2867 strcatftime(str, format, time, locale);
2868 }
2869
2879 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2880 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)
2881 {
2882 std::basic_string<T, TR, AX> str;
2883 strcatftime(str, format, time, locale);
2884 return str;
2885 }
2886
2892 //template<class T>
2893 //void strlwr(_Inout_z_ T* str)
2894 //{
2895 // stdex_assert(str);
2896 // for (size_t i = 0; str[i]; ++i)
2897 // str[i] = tolower(str[i]);
2898 //}
2899
2906 //template<class T>
2907 //void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
2908 //{
2909 // stdex_assert(str);
2910 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2911 // for (size_t i = 0; str[i]; ++i)
2912 // str[i] = ctype.tolower(str[i]);
2913 //}
2914
2921 template<class T>
2922 void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2923 {
2924 stdex_assert(str || !count);
2925 for (size_t i = 0; i < count && str[i]; ++i)
2926 str[i] = tolower(str[i]);
2927 }
2928
2936 template<class T>
2937 void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2938 {
2939 stdex_assert(str || !count);
2940 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2941 for (size_t i = 0; i < count && str[i]; ++i)
2942 str[i] = ctype.tolower(str[i]);
2943 }
2944
2950 template<class T, size_t N>
2951 void strlwr(_Inout_ T (&str)[N])
2952 {
2953 strlwr(str, N);
2954 }
2955
2962 template<class T, size_t N>
2963 void strlwr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2964 {
2965 strlwr(str, N, locale);
2966 }
2967
2973 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2974 void strlwr(_Inout_ std::basic_string<T, TR, AX>& str)
2975 {
2976 for (auto& c : str)
2977 c = tolower(c);
2978 }
2979
2986 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2987 void strlwr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2988 {
2989 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2990 for (auto& c : str)
2991 c = ctype.tolower(c);
2992 }
2993
2999 //template<class T>
3000 //void strupr(_Inout_z_ T* str)
3001 //{
3002 // stdex_assert(str);
3003 // for (size_t i = 0; str[i]; ++i)
3004 // str[i] = toupper(str[i]);
3005 //}
3006
3013 //template<class T>
3014 //void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
3015 //{
3016 // stdex_assert(str);
3017 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3018 // for (size_t i = 0; str[i]; ++i)
3019 // str[i] = ctype.toupper(str[i]);
3020 //}
3021
3028 template<class T>
3029 void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count)
3030 {
3031 stdex_assert(str || !count);
3032 for (size_t i = 0; i < count && str[i]; ++i)
3033 str[i] = toupper(str[i]);
3034 }
3035
3043 template<class T>
3044 void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
3045 {
3046 stdex_assert(str || !count);
3047 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3048 for (size_t i = 0; i < count && str[i]; ++i)
3049 str[i] = ctype.toupper(str[i]);
3050 }
3051
3057 template<class T, size_t N>
3058 void strupr(_Inout_ T (&str)[N])
3059 {
3060 return strupr(str, N);
3061 }
3062
3069 template<class T, size_t N>
3070 void strupr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
3071 {
3072 return strupr(str, N, locale);
3073 }
3074
3080 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3081 void strupr(_Inout_ std::basic_string<T, TR, AX>& str)
3082 {
3083 for (auto& c : str)
3084 c = toupper(c);
3085 }
3086
3093 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3094 void strupr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
3095 {
3096 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3097 for (auto& c : str)
3098 c = ctype.toupper(c);
3099 }
3100
3109 template<class T>
3110 size_t ltrim(
3111 _Inout_z_count_(count) T* str, _In_ size_t count)
3112 {
3113 for (size_t i = 0;; ++i) {
3114 if (i >= count) {
3115 if (count) str[0] = 0;
3116 return 0;
3117 }
3118 if (!str[i]) {
3119 str[0] = 0;
3120 return 0;
3121 }
3122 if (!isspace(str[i])) {
3123 if (!i)
3124 return strnlen(str, count);
3125 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
3126 str[n] = 0;
3127 return n;
3128 }
3129 }
3130 }
3131
3141 template<class T>
3142 size_t ltrim(
3143 _Inout_z_count_(count) T* str, _In_ size_t count,
3144 _In_ const std::locale& locale)
3145 {
3146 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3147 for (size_t i = 0;; ++i) {
3148 if (i >= count) {
3149 if (count) str[0] = 0;
3150 return 0;
3151 }
3152 if (!str[i]) {
3153 str[0] = 0;
3154 return 0;
3155 }
3156 if (!ctype.is(ctype.space, str[i])) {
3157 if (!i)
3158 return strnlen(str, count);
3159 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
3160 str[n] = 0;
3161 return n;
3162 }
3163 }
3164 }
3165
3171 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3172 void ltrim(_Inout_ std::basic_string<T, TR, AX>& s)
3173 {
3174 s.erase(
3175 s.begin(),
3176 std::find_if(
3177 s.begin(),
3178 s.end(),
3179 [&](_In_ T ch) { return !isspace(ch); }));
3180 }
3181
3188 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3189 void ltrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3190 {
3191 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3192 s.erase(
3193 s.begin(),
3194 std::find_if(
3195 s.begin(),
3196 s.end(),
3197 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }));
3198 }
3199
3208 template<class T>
3209 size_t rtrim(
3210 _Inout_z_count_(count) T* str, _In_ size_t count)
3211 {
3212 for (size_t i = 0, j = 0;;) {
3213 if (i >= count || !str[i]) {
3214 if (j < count) str[j] = 0;
3215 return j;
3216 }
3217 if (!isspace(str[i]))
3218 j = ++i;
3219 else
3220 ++i;
3221 }
3222 }
3223
3233 template<class T>
3234 size_t rtrim(
3235 _Inout_z_count_(count) T* str, _In_ size_t count,
3236 _In_ const std::locale& locale)
3237 {
3238 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3239 for (size_t i = 0, j = 0;;) {
3240 if (i >= count || !str[i]) {
3241 if (j < count) str[j] = 0;
3242 return j;
3243 }
3244 if (!ctype.is(ctype.space, str[i]))
3245 j = ++i;
3246 else
3247 ++i;
3248 }
3249 }
3250
3256 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3257 void rtrim(_Inout_ std::basic_string<T, TR, AX>& s)
3258 {
3259 s.erase(
3260 std::find_if(
3261 s.rbegin(),
3262 s.rend(),
3263 [&](_In_ T ch) { return !isspace(ch); }).base(),
3264 s.end());
3265 }
3266
3273 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3274 void rtrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3275 {
3276 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3277 s.erase(
3278 std::find_if(
3279 s.rbegin(),
3280 s.rend(),
3281 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }).base(),
3282 s.end());
3283 }
3284
3293 template<class T>
3294 size_t trim(
3295 _Inout_z_count_(count) T* str, _In_ size_t count)
3296 {
3297 return ltrim(str, rtrim(str, count));
3298 }
3299
3309 template<class T>
3310 size_t trim(
3311 _Inout_z_count_(count) T* str, _In_ size_t count,
3312 _In_ const std::locale& locale)
3313 {
3314 return ltrim(str, rtrim(str, count, locale), locale);
3315 }
3316
3322 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3323 void trim(_Inout_ std::basic_string<T, TR, AX>& s)
3324 {
3325 auto nonspace = [&](_In_ T ch) { return !isspace(ch); };
3326 s.erase(
3327 s.begin(),
3328 std::find_if(
3329 s.begin(),
3330 s.end(),
3331 nonspace));
3332 s.erase(
3333 std::find_if(
3334 s.rbegin(),
3335 s.rend(),
3336 nonspace).base(),
3337 s.end());
3338 }
3339
3346 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3347 void trim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3348 {
3349 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3350 auto nonspace = [&](_In_ T ch) { return !ctype.is(ctype.space, ch); };
3351 s.erase(
3352 s.begin(),
3353 std::find_if(
3354 s.begin(),
3355 s.end(),
3356 nonspace));
3357 s.erase(
3358 std::find_if(
3359 s.rbegin(),
3360 s.rend(),
3361 nonspace).base(),
3362 s.end());
3363 }
3364}