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
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 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
1648 template <class T1, class T2>
1649 size_t strncpy(
1650 _Out_writes_(count) _Post_maybez_ T1* dst,
1651 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1652 {
1653 stdex_assert(dst || !count);
1654 stdex_assert(src || !count);
1655 for (size_t i = 0; ; ++i) {
1656 if (i >= count)
1657 return i;
1658 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1659 return i;
1660 }
1661 }
1662
1673 template <class T1, class T2>
1674 size_t strncpy(
1675 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1676 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1677 {
1678 stdex_assert(dst || !count_dst);
1679 stdex_assert(src || !count_src);
1680 for (size_t i = 0; ; ++i)
1681 {
1682 if (i >= count_dst)
1683 return i;
1684 if (i >= count_src) {
1685 dst[i] = 0;
1686 return i;
1687 }
1688 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1689 return i;
1690 }
1691 }
1692
1701 template <class T1, size_t N1, class T2, size_t N2>
1702 size_t strncpy(
1703 _Out_ _Post_maybez_ T1 (&dst)[N1],
1704 _In_ const T2 (&src)[N2])
1705 {
1706 return strncpy(dst, N1, src, N2);
1707 }
1708
1717 template <class T1, class T2>
1718 size_t strcat(
1719 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
1720 _In_z_ const T2* src)
1721 {
1722 stdex_assert(dst);
1723 stdex_assert(src);
1724 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1725 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1726 return j;
1727 }
1728 }
1729
1739 template <class T1, class T2>
1740 size_t strncat(
1741 _Inout_z_ T1* dst,
1742 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1743 {
1744 stdex_assert(dst || !count);
1745 stdex_assert(src || !count);
1746 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1747 if (i >= count)
1748 return j;
1749 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1750 return j;
1751 }
1752 }
1753
1764 template <class T1, class T2>
1765 size_t strncat(
1766 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1767 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1768 {
1769 stdex_assert(dst || !count_dst);
1770 stdex_assert(src || !count_src);
1771 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
1772 {
1773 if (j >= count_dst)
1774 return j;
1775 if (i >= count_src) {
1776 dst[j] = 0;
1777 return j;
1778 }
1779 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1780 return j;
1781 }
1782 }
1783
1794 template <class T>
1795 _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
1796 {
1797 if (!str) _Unlikely_
1798 return nullptr;
1799 size_t count = strlen(str) + 1;
1800 T* dst = new T[count];
1801 strncpy(dst, count, str, SIZE_MAX);
1802 return dst;
1803 }
1804
1816 template <class T>
1817 _Ret_z_ T* strndup(
1818 _In_reads_or_z_opt_(count) const T* str,
1819 _In_ size_t count)
1820 {
1821 T* dst = new T[count];
1822 strncpy(dst, count, str, SIZE_MAX);
1823 return dst;
1824 }
1825
1836 template <class T, size_t N>
1837 _Check_return_ _Ret_maybenull_z_ T* strndup(_In_ const T (&str)[N])
1838 {
1839 return strndup(str, N);
1840 }
1841
1851 template <class T>
1852 size_t crlf2nl(_Out_writes_z_(_String_length_(src) + 1) T* dst, _In_z_ const T* src)
1853 {
1854 stdex_assert(dst);
1855 stdex_assert(src);
1856 size_t i, j;
1857 for (i = j = 0; src[j];) {
1858 if (src[j] != '\r' || src[j + 1] != '\n')
1859 dst[i++] = src[j++];
1860 else {
1861 dst[i++] = '\n';
1862 j += 2;
1863 }
1864 }
1865 dst[i] = 0;
1866 return i;
1867 }
1868
1875 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1876 void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& dst, _In_z_ const T* src)
1877 {
1878 stdex_assert(src);
1879 stdex_assert(src != dst.data());
1880 dst.clear();
1881 dst.reserve(strlen(src));
1882 for (size_t j = 0; src[j];) {
1883 if (src[j] != '\r' || src[j + 1] != '\n')
1884 dst += src[j++];
1885 else {
1886 dst += '\n';
1887 j += 2;
1888 }
1889 }
1890 }
1891
1897 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1898 void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& str)
1899 {
1900 size_t i, j, n;
1901 for (i = j = 0, n = str.size(); j < n;) {
1902 if (str[j] != '\r' || str[j + 1] != '\n')
1903 str[i++] = str[j++];
1904 else {
1905 str[i++] = '\n';
1906 j += 2;
1907 }
1908 }
1909 str.resize(i);
1910 }
1911
1913 template <class T, class T_bin>
1914 T_bin strtoint(
1915 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1916 _Out_opt_ size_t* end,
1917 _In_ int radix,
1918 _Out_ uint8_t& flags)
1919 {
1920 stdex_assert(str || !count);
1921 stdex_assert(radix == 0 || 2 <= radix && radix <= 36);
1922
1923 size_t i = 0;
1924 T_bin value = 0, digit,
1925 max_ui = (T_bin)-1,
1926 max_ui_pre1, max_ui_pre2;
1927
1928 flags = 0;
1929
1930 // Skip leading spaces.
1931 for (;; ++i) {
1932 if (i >= count || !str[i]) goto error;
1933 if (!isspace(str[i])) break;
1934 }
1935
1936 // Read the sign.
1937 if (str[i] == '+') {
1938 flags &= ~0x01;
1939 ++i;
1940 if (i >= count || !str[i]) goto error;
1941 }
1942 else if (str[i] == '-') {
1943 flags |= 0x01;
1944 ++i;
1945 if (i >= count || !str[i]) goto error;
1946 }
1947
1948 if (radix == 16) {
1949 // On hexadecimal, allow leading 0x.
1950 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
1951 i += 2;
1952 if (i >= count || !str[i]) goto error;
1953 }
1954 }
1955 else if (!radix) {
1956 // Autodetect radix.
1957 if (str[i] == '0') {
1958 ++i;
1959 if (i >= count || !str[i]) goto error;
1960 if (str[i] == 'x' || str[i] == 'X') {
1961 radix = 16;
1962 ++i;
1963 if (i >= count || !str[i]) goto error;
1964 }
1965 else
1966 radix = 8;
1967 }
1968 else
1969 radix = 10;
1970 }
1971
1972 // We have the radix.
1973 max_ui_pre1 = max_ui / (T_bin)radix;
1974 max_ui_pre2 = max_ui % (T_bin)radix;
1975 for (;;) {
1976 if ('0' <= str[i] && str[i] <= '9')
1977 digit = (T_bin)str[i] - '0';
1978 else if ('A' <= str[i] && str[i] <= 'Z')
1979 digit = (T_bin)str[i] - 'A' + '\x0a';
1980 else if ('a' <= str[i] && str[i] <= 'z')
1981 digit = (T_bin)str[i] - 'a' + '\x0a';
1982 else
1983 goto error;
1984 if (digit >= (T_bin)radix)
1985 goto error;
1986
1987 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
1988 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
1989 value = value * (T_bin)radix + digit;
1990 else {
1991 // Overflow!
1992 flags |= 0x02;
1993 }
1994
1995 ++i;
1996 if (i >= count || !str[i])
1997 goto error;
1998 }
1999
2000 error:
2001 if (end) *end = i;
2002 return value;
2003 }
2005
2016 template <class T, class T_bin>
2017 T_bin strtoint(
2018 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2019 _Out_opt_ size_t* end,
2020 _In_ int radix)
2021 {
2022 uint8_t flags;
2023 T_bin value;
2024
2025 switch (sizeof(T_bin)) {
2026 case 1:
2027 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
2028 if ((flags & 0x01) && (value & 0x80)) {
2029 // Sign bit is 1 => overflow.
2030 flags |= 0x02;
2031 }
2032 return (flags & 0x02) ?
2033 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
2034 (flags & 0x01) ? -value : value;
2035
2036 case 2:
2037 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
2038 if ((flags & 0x01) && (value & 0x8000)) {
2039 // Sign bit is 1 => overflow.
2040 flags |= 0x02;
2041 }
2042 return (flags & 0x02) ?
2043 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
2044 (flags & 0x01) ? -value : value;
2045
2046 case 4:
2047 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
2048 if ((flags & 0x01) && (value & 0x80000000)) {
2049 // Sign bit is 1 => overflow.
2050 flags |= 0x02;
2051 }
2052 return (flags & 0x02) ?
2053 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
2054 (flags & 0x01) ? -value : value;
2055
2056 case 8:
2057 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
2058 if ((flags & 0x01) && (value & 0x8000000000000000)) {
2059 // Sign bit is 1 => overflow.
2060 flags |= 0x02;
2061 }
2062 return (flags & 0x02) ?
2063 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
2064 (flags & 0x01) ? -value : value;
2065
2066 default:
2067 throw std::invalid_argument("Unsupported bit length");
2068 }
2069 }
2070
2080 template <class T, size_t N, class T_bin>
2081 T_bin strtoint(
2082 _In_ const T (&str)[N],
2083 _Out_opt_ size_t* end,
2084 _In_ int radix)
2085 {
2086 return strtoint<T, T_bin>(str, N, end, radix);
2087 }
2088
2099 template <class T, class T_bin>
2100 T_bin strtouint(
2101 _In_reads_or_z_opt_(count) const T* str,
2102 _In_ size_t count,
2103 _Out_opt_ size_t* end,
2104 _In_ int radix)
2105 {
2106 uint8_t flags;
2107 T_bin value;
2108
2109 switch (sizeof(T_bin)) {
2110 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
2111 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
2112 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
2113 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
2114 default: throw std::invalid_argument("Unsupported bit length");
2115 }
2116
2117 return (flags & 0x02) ?
2118 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
2119 (flags & 0x01) ? ~value : value;
2120 }
2121
2131 template <class T, size_t N, class T_bin>
2132 T_bin strtouint(
2133 _In_ const T (&str)[N],
2134 _Out_opt_ size_t* end,
2135 _In_ int radix)
2136 {
2137 return strtouint<T, T_bin>(str, N, end, radix);
2138 }
2139
2150 template <class T>
2151 int8_t strto8(
2152 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2153 _Out_opt_ size_t* end,
2154 _In_ int radix)
2155 {
2156 return strtoint<T, int8_t>(str, count, end, radix);
2157 }
2158
2168 template <class T, size_t N>
2169 int8_t strto8(
2170 _In_ const T (&str)[N],
2171 _Out_opt_ size_t* end,
2172 _In_ int radix)
2173 {
2174 return strto8<T>(str, N, end, radix);
2175 }
2176
2187 template <class T>
2188 int16_t strto16(
2189 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2190 _Out_opt_ size_t* end,
2191 _In_ int radix)
2192 {
2193 return strtoint<T, int16_t>(str, count, end, radix);
2194 }
2195
2205 template <class T, size_t N>
2206 int16_t strto16(
2207 _In_ const T (&str)[N],
2208 _Out_opt_ size_t* end,
2209 _In_ int radix)
2210 {
2211 return strto16<T>(str, N, end, radix);
2212 }
2213
2224 template <class T>
2225 int32_t strto32(
2226 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2227 _Out_opt_ size_t* end,
2228 _In_ int radix)
2229 {
2230 return strtoint<T, int32_t>(str, count, end, radix);
2231 }
2232
2242 template <class T, size_t N>
2243 int32_t strto32(
2244 _In_ const T (&str)[N],
2245 _Out_opt_ size_t* end,
2246 _In_ int radix)
2247 {
2248 return strto32<T>(str, N, end, radix);
2249 }
2250
2261 template <class T>
2262 int64_t strto64(
2263 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2264 _Out_opt_ size_t* end,
2265 _In_ int radix)
2266 {
2267 return strtoint<T, int64_t>(str, count, end, radix);
2268 }
2269
2279 template <class T, size_t N>
2280 int64_t strto64(
2281 _In_ const T (&str)[N],
2282 _Out_opt_ size_t* end,
2283 _In_ int radix)
2284 {
2285 return strto64<T>(str, N, end, radix);
2286 }
2287
2299 template <class T>
2300 ptrdiff_t strtoi(
2301 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2302 _Out_opt_ size_t* end,
2303 _In_ int radix)
2304 {
2305#if defined(_WIN64) || defined(__LP64__)
2306 return static_cast<ptrdiff_t>(strto64(str, count, end, radix));
2307#else
2308 return static_cast<ptrdiff_t>(strto32(str, count, end, radix));
2309#endif
2310 }
2311
2322 template <class T, size_t N>
2323 ptrdiff_t strtoi(
2324 _In_ const T (&str)[N],
2325 _Out_opt_ size_t* end,
2326 _In_ int radix)
2327 {
2328 return strtoi<T>(str, N, end, radix);
2329 }
2330
2341 template <class T>
2342 uint8_t strtou8(
2343 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2344 _Out_opt_ size_t* end,
2345 _In_ int radix)
2346 {
2347 return strtouint<T, uint8_t>(str, count, end, radix);
2348 }
2349
2359 template <class T, size_t N>
2360 uint8_t strtou8(
2361 _In_ const T (&str)[N],
2362 _Out_opt_ size_t* end,
2363 _In_ int radix)
2364 {
2365 return strtou8(str, N, end, radix);
2366 }
2367
2378 template <class T>
2379 uint16_t strtou16(
2380 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2381 _Out_opt_ size_t* end,
2382 _In_ int radix)
2383 {
2384 return strtouint<T, uint16_t>(str, count, end, radix);
2385 }
2386
2396 template <class T, size_t N>
2397 uint16_t strtou16(
2398 _In_ const T (&str)[N],
2399 _Out_opt_ size_t* end,
2400 _In_ int radix)
2401 {
2402 return strtou16(str, N, end, radix);
2403 }
2404
2415 template <class T>
2416 uint32_t strtou32(
2417 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2418 _Out_opt_ size_t* end,
2419 _In_ int radix)
2420 {
2421 return strtouint<T, uint32_t>(str, count, end, radix);
2422 }
2423
2433 template <class T, size_t N>
2434 uint32_t strtou32(
2435 _In_ const T (&str)[N],
2436 _Out_opt_ size_t* end,
2437 _In_ int radix)
2438 {
2439 return strtou32(str, N, end, radix);
2440 }
2441
2452 template <class T>
2453 uint64_t strtou64(
2454 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2455 _Out_opt_ size_t* end,
2456 _In_ int radix)
2457 {
2458 return strtouint<T, uint64_t>(str, count, end, radix);
2459 }
2460
2470 template <class T, size_t N>
2471 uint64_t strtou64(
2472 _In_ const T (&str)[N],
2473 _Out_opt_ size_t* end,
2474 _In_ int radix)
2475 {
2476 return strtou64<T>(str, N, end, radix);
2477 }
2478
2490 template <class T>
2491 size_t strtoui(
2492 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2493 _Out_opt_ size_t* end,
2494 _In_ int radix)
2495 {
2496#if defined(_WIN64) || defined(__LP64__)
2497 return static_cast<size_t>(strtou64(str, count, end, radix));
2498#else
2499 return static_cast<size_t>(strtou32(str, count, end, radix));
2500#endif
2501 }
2502
2513 template <class T, size_t N>
2514 size_t strtoui(
2515 _In_ const T (&str)[N],
2516 _Out_opt_ size_t* end,
2517 _In_ int radix)
2518 {
2519 return strtoui<T>(str, N, end, radix);
2520 }
2521
2532 inline double strtod(
2533 _In_reads_or_z_opt_(count) const char* str, _In_ size_t count,
2534 _Out_opt_ size_t* end,
2535 _In_opt_ locale_t locale)
2536 {
2537 count = strnlen(str, count);
2538 stdex_assert(str || !count);
2539 std::string tmp(str, count);
2540 char* _end;
2541 double r;
2542#if _WIN32
2543 r = _strtod_l(tmp.c_str(), &_end, locale);
2544#else
2545 r = strtod_l(tmp.c_str(), &_end, locale);
2546#endif
2547 if (end) *end = (size_t)(_end - tmp.c_str());
2548 return r;
2549 }
2550
2561 inline double strtod(
2562 _In_reads_or_z_opt_(count) const wchar_t* str, _In_ size_t count,
2563 _Out_opt_ size_t* end,
2564 _In_opt_ locale_t locale)
2565 {
2566 count = strnlen(str, count);
2567 stdex_assert(str || !count);
2568 std::wstring tmp(str, count);
2569 wchar_t* _end;
2570 double r;
2571#if _WIN32
2572 r = _wcstod_l(tmp.c_str(), &_end, locale);
2573#else
2574 r = wcstod_l(tmp.c_str(), &_end, locale);
2575#endif
2576 if (end) *end = (size_t)(_end - tmp.c_str());
2577 return r;
2578 }
2579
2581 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)
2582 {
2583#ifdef _WIN32
2584#pragma warning(suppress: 4996)
2585 return _vsnprintf_l(str, capacity, format, locale, arg);
2586#else
2587 va_list arg_mutable;
2588 va_copy(arg_mutable, arg);
2589 return ::vsnprintf_l(str, capacity, locale, format, arg_mutable);
2590#endif
2591 }
2592
2593 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)
2594 {
2595#ifdef _WIN32
2596#pragma warning(suppress: 4996)
2597 return _vsnwprintf_l(str, capacity, format, locale, arg);
2598#else
2599 va_list arg_mutable;
2600 va_copy(arg_mutable, arg);
2601 return ::vswprintf_l(str, capacity, locale, format, arg_mutable);
2602#endif
2603 }
2605
2616 template<class T, class TR, class AX>
2617 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)
2618 {
2619 T buf[1024 / sizeof(T)];
2620
2621 // Try with stack buffer first.
2622 int count = vsnprintf(buf, _countof(buf), format, locale, arg);
2623 if (0 <= count && static_cast<size_t>(count) <= _countof(buf)) {
2624 // Copy from stack.
2625 str.append(buf, static_cast<size_t>(count));
2626 return static_cast<size_t>(count);
2627 }
2628#ifdef _WIN32
2629 if (count < 0) {
2630 switch (errno) {
2631 case 0:
2632 count = vsnprintf(NULL, 0, format, locale, arg);
2633 stdex_assert(count >= 0);
2634 break;
2635 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2636 case EILSEQ: throw std::runtime_error("encoding error");
2637 default: throw std::runtime_error("failed to format string");
2638 }
2639 }
2640 size_t offset = str.size();
2641 str.resize(offset + count);
2642 if (vsnprintf(&str[offset], count + 1, format, locale, arg) != count) _Unlikely_
2643 throw std::runtime_error("failed to format string");
2644#else
2645 size_t offset = str.size();
2646 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2647 switch (errno) {
2648 case EOVERFLOW:
2649 // Allocate on heap and retry.
2650 str.resize(offset + capacity);
2651 count = vsnprintf(&str[offset], capacity, format, locale, arg);
2652 if (0 <= count && static_cast<size_t>(count) <= capacity) {
2653 str.resize(offset + static_cast<size_t>(count));
2654 return static_cast<size_t>(count);
2655 }
2656 break;
2657 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2658 case EILSEQ: throw std::runtime_error("encoding error");
2659 default: throw std::runtime_error("failed to format string");
2660 }
2661 }
2662#endif
2663 return static_cast<size_t>(count);
2664 }
2665
2675 template<class T, class TR, class AX>
2676 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, ...)
2677 {
2678 va_list arg;
2679 va_start(arg, locale);
2680 size_t n = vappendf(str, format, locale, arg);
2681 va_end(arg);
2682 return n;
2683 }
2684
2693 template<class T, class TR, class AX>
2694 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)
2695 {
2696 str.clear();
2697 vappendf(str, format, locale, arg);
2698 }
2699
2707 template<class T, class TR, class AX>
2708 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, ...)
2709 {
2710 va_list arg;
2711 va_start(arg, locale);
2712 vsprintf(str, format, locale, arg);
2713 va_end(arg);
2714 }
2715
2725 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2726 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)
2727 {
2728 std::basic_string<T, TR, AX> str;
2729 vappendf(str, format, locale, arg);
2730 return str;
2731 }
2732
2741 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2742 std::basic_string<T, TR, AX> sprintf(_In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, ...)
2743 {
2744 va_list arg;
2745 va_start(arg, locale);
2746 auto str = vsprintf(format, locale, arg);
2747 va_end(arg);
2748 return str;
2749 }
2750
2752 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)
2753 {
2754#ifdef _WIN32
2755 return _strftime_l(str, capacity, format, time, locale);
2756#else
2757 return strftime_l(str, capacity, format, time, locale);
2758#endif
2759 }
2760
2761 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)
2762 {
2763#ifdef _WIN32
2764 return _wcsftime_l(str, capacity, format, time, locale);
2765#else
2766 return wcsftime_l(str, capacity, format, time, locale);
2767#endif
2768 }
2770
2779 template<class T, class TR, class AX>
2780 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)
2781 {
2782 T buf[1024 / sizeof(T)];
2783
2784 // Try with stack buffer first.
2785 size_t count = strftime(buf, _countof(buf), format, time, locale);
2786 if (count) {
2787 // Copy from stack.
2788 str.append(buf, count);
2789 return;
2790 }
2791 size_t offset = str.size();
2792 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2793 // Allocate on heap and retry.
2794 str.resize(offset + capacity);
2795 count = strftime(&str[offset], capacity + 1, format, time, locale);
2796 if (count) {
2797 str.resize(offset + count);
2798 return;
2799 }
2800 }
2801 }
2802
2811 template<class T, class TR, class AX>
2812 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)
2813 {
2814 str.clear();
2815 strcatftime(str, format, time, locale);
2816 }
2817
2827 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2828 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)
2829 {
2830 std::basic_string<T, TR, AX> str;
2831 strcatftime(str, format, time, locale);
2832 return str;
2833 }
2834
2840 //template<class T>
2841 //void strlwr(_Inout_z_ T* str)
2842 //{
2843 // stdex_assert(str);
2844 // for (size_t i = 0; str[i]; ++i)
2845 // str[i] = tolower(str[i]);
2846 //}
2847
2854 //template<class T>
2855 //void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
2856 //{
2857 // stdex_assert(str);
2858 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2859 // for (size_t i = 0; str[i]; ++i)
2860 // str[i] = ctype.tolower(str[i]);
2861 //}
2862
2869 template<class T>
2870 void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2871 {
2872 stdex_assert(str || !count);
2873 for (size_t i = 0; i < count && str[i]; ++i)
2874 str[i] = tolower(str[i]);
2875 }
2876
2884 template<class T>
2885 void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2886 {
2887 stdex_assert(str || !count);
2888 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2889 for (size_t i = 0; i < count && str[i]; ++i)
2890 str[i] = ctype.tolower(str[i]);
2891 }
2892
2898 template<class T, size_t N>
2899 void strlwr(_Inout_ T (&str)[N])
2900 {
2901 strlwr(str, N);
2902 }
2903
2910 template<class T, size_t N>
2911 void strlwr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2912 {
2913 strlwr(str, N, locale);
2914 }
2915
2921 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2922 void strlwr(_Inout_ std::basic_string<T, TR, AX>& str)
2923 {
2924 for (auto& c : str)
2925 c = tolower(c);
2926 }
2927
2934 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2935 void strlwr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2936 {
2937 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2938 for (auto& c : str)
2939 c = ctype.tolower(c);
2940 }
2941
2947 //template<class T>
2948 //void strupr(_Inout_z_ T* str)
2949 //{
2950 // stdex_assert(str);
2951 // for (size_t i = 0; str[i]; ++i)
2952 // str[i] = toupper(str[i]);
2953 //}
2954
2961 //template<class T>
2962 //void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
2963 //{
2964 // stdex_assert(str);
2965 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2966 // for (size_t i = 0; str[i]; ++i)
2967 // str[i] = ctype.toupper(str[i]);
2968 //}
2969
2976 template<class T>
2977 void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2978 {
2979 stdex_assert(str || !count);
2980 for (size_t i = 0; i < count && str[i]; ++i)
2981 str[i] = toupper(str[i]);
2982 }
2983
2991 template<class T>
2992 void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2993 {
2994 stdex_assert(str || !count);
2995 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2996 for (size_t i = 0; i < count && str[i]; ++i)
2997 str[i] = ctype.toupper(str[i]);
2998 }
2999
3005 template<class T, size_t N>
3006 void strupr(_Inout_ T (&str)[N])
3007 {
3008 return strupr(str, N);
3009 }
3010
3017 template<class T, size_t N>
3018 void strupr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
3019 {
3020 return strupr(str, N, locale);
3021 }
3022
3028 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3029 void strupr(_Inout_ std::basic_string<T, TR, AX>& str)
3030 {
3031 for (auto& c : str)
3032 c = toupper(c);
3033 }
3034
3041 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3042 void strupr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
3043 {
3044 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3045 for (auto& c : str)
3046 c = ctype.toupper(c);
3047 }
3048
3057 template<class T>
3058 size_t ltrim(
3059 _Inout_z_count_(count) T* str, _In_ size_t count)
3060 {
3061 for (size_t i = 0;; ++i) {
3062 if (i >= count) {
3063 if (count) str[0] = 0;
3064 return 0;
3065 }
3066 if (!str[i]) {
3067 str[0] = 0;
3068 return 0;
3069 }
3070 if (!isspace(str[i])) {
3071 if (!i)
3072 return strnlen(str, count);
3073 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
3074 str[n] = 0;
3075 return n;
3076 }
3077 }
3078 }
3079
3089 template<class T>
3090 size_t ltrim(
3091 _Inout_z_count_(count) T* str, _In_ size_t count,
3092 _In_ const std::locale& locale)
3093 {
3094 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3095 for (size_t i = 0;; ++i) {
3096 if (i >= count) {
3097 if (count) str[0] = 0;
3098 return 0;
3099 }
3100 if (!str[i]) {
3101 str[0] = 0;
3102 return 0;
3103 }
3104 if (!ctype.is(ctype.space, str[i])) {
3105 if (!i)
3106 return strnlen(str, count);
3107 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
3108 str[n] = 0;
3109 return n;
3110 }
3111 }
3112 }
3113
3119 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3120 void ltrim(_Inout_ std::basic_string<T, TR, AX>& s)
3121 {
3122 s.erase(
3123 s.begin(),
3124 std::find_if(
3125 s.begin(),
3126 s.end(),
3127 [&](_In_ T ch) { return !isspace(ch); }));
3128 }
3129
3136 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3137 void ltrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3138 {
3139 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3140 s.erase(
3141 s.begin(),
3142 std::find_if(
3143 s.begin(),
3144 s.end(),
3145 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }));
3146 }
3147
3156 template<class T>
3157 size_t rtrim(
3158 _Inout_z_count_(count) T* str, _In_ size_t count)
3159 {
3160 for (size_t i = 0, j = 0;;) {
3161 if (i >= count || !str[i]) {
3162 if (j < count) str[j] = 0;
3163 return j;
3164 }
3165 if (!isspace(str[i]))
3166 j = ++i;
3167 else
3168 ++i;
3169 }
3170 }
3171
3181 template<class T>
3182 size_t rtrim(
3183 _Inout_z_count_(count) T* str, _In_ size_t count,
3184 _In_ const std::locale& locale)
3185 {
3186 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3187 for (size_t i = 0, j = 0;;) {
3188 if (i >= count || !str[i]) {
3189 if (j < count) str[j] = 0;
3190 return j;
3191 }
3192 if (!ctype.is(ctype.space, str[i]))
3193 j = ++i;
3194 else
3195 ++i;
3196 }
3197 }
3198
3204 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3205 void rtrim(_Inout_ std::basic_string<T, TR, AX>& s)
3206 {
3207 s.erase(
3208 std::find_if(
3209 s.rbegin(),
3210 s.rend(),
3211 [&](_In_ T ch) { return !isspace(ch); }).base(),
3212 s.end());
3213 }
3214
3221 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3222 void rtrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3223 {
3224 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3225 s.erase(
3226 std::find_if(
3227 s.rbegin(),
3228 s.rend(),
3229 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }).base(),
3230 s.end());
3231 }
3232
3241 template<class T>
3242 size_t trim(
3243 _Inout_z_count_(count) T* str, _In_ size_t count)
3244 {
3245 return ltrim(str, rtrim(str, count));
3246 }
3247
3257 template<class T>
3258 size_t trim(
3259 _Inout_z_count_(count) T* str, _In_ size_t count,
3260 _In_ const std::locale& locale)
3261 {
3262 return ltrim(str, rtrim(str, count, locale), locale);
3263 }
3264
3270 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3271 void trim(_Inout_ std::basic_string<T, TR, AX>& s)
3272 {
3273 auto nonspace = [&](_In_ T ch) { return !isspace(ch); };
3274 s.erase(
3275 s.begin(),
3276 std::find_if(
3277 s.begin(),
3278 s.end(),
3279 nonspace));
3280 s.erase(
3281 std::find_if(
3282 s.rbegin(),
3283 s.rend(),
3284 nonspace).base(),
3285 s.end());
3286 }
3287
3294 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3295 void trim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3296 {
3297 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3298 auto nonspace = [&](_In_ T ch) { return !ctype.is(ctype.space, ch); };
3299 s.erase(
3300 s.begin(),
3301 std::find_if(
3302 s.begin(),
3303 s.end(),
3304 nonspace));
3305 s.erase(
3306 std::find_if(
3307 s.rbegin(),
3308 s.rend(),
3309 nonspace).base(),
3310 s.end());
3311 }
3312}