stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
string.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2016-2024 Amebis
4*/
5
6#pragma once
7
8#include "compat.hpp"
9#include "locale.hpp"
10#include <ctype.h>
11#include <stdarg.h>
12#include <stdarg.h>
13#include <stdint.h>
14#include <stdio.h>
15#include <time.h>
16#if defined(__APPLE__)
17#include <xlocale.h>
18#endif
19#include <algorithm>
20#include <climits>
21#include <locale>
22#include <stdexcept>
23
24namespace stdex
25{
29#ifdef _WIN32
30 using utf16_t = wchar_t;
31 using utf32_t = char32_t;
32#else
33 using utf16_t = char16_t;
34 using utf32_t = wchar_t;
35#endif
36
42 inline bool is_high_surrogate(_In_ utf16_t chr)
43 {
44 return 0xd800 < chr && chr < 0xdc00;
45 }
46
52 inline bool is_low_surrogate(_In_ utf16_t chr)
53 {
54 return 0xdc00 < chr && chr < 0xe000;
55 }
56
62 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
63 {
64 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
65 }
66
72 inline utf32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
73 {
74 _Assume_(is_surrogate_pair(str));
75 return
76 (static_cast<utf32_t>(str[0] - 0xd800) << 10) +
77 static_cast<utf32_t>(str[1] - 0xdc00) +
78 0x10000;
79 }
80
86 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ utf32_t chr)
87 {
88 _Assume_(chr >= 0x10000);
89 chr -= 0x10000;
90 str[0] = 0xd800 + static_cast<utf32_t>((chr >> 10) & 0x3ff);
91 str[1] = 0xdc00 + static_cast<utf32_t>(chr & 0x3ff);
92 }
93
99 inline bool iscombining(_In_ utf32_t chr)
100 {
101 return
102 (0x0300 <= chr && chr < 0x0370) ||
103 (0x1dc0 <= chr && chr < 0x1e00) ||
104 (0x20d0 <= chr && chr < 0x2100) ||
105 (0xfe20 <= chr && chr < 0xfe30);
106 }
107
113 template <class T>
114 inline bool islbreak(_In_ T chr)
115 {
116 return chr == '\n' || chr == '\r';
117 }
118
127 template <class T>
128 inline size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
129 {
130 _Assume_(chr || !count);
131 if (count >= 2 && ((chr[0] == '\r' && chr[1] == '\n') || (chr[0] == '\n' && chr[1] == '\r')))
132 return 2;
133 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
134 return 1;
135 return 0;
136 }
137
143 template <class T>
144 inline bool isspace(_In_ T chr)
145 {
146 return chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' || chr == '\v' || chr == '\f';
147 }
148
154 template <class T>
155 inline bool islower(_In_ T chr)
156 {
157 return 'a' <= chr && chr <= 'z';
158 }
159
165 template <class T>
166 inline bool isupper(_In_ T chr)
167 {
168 return 'A' <= chr && chr <= 'Z';
169 }
170
176 template <class T>
177 inline bool isdigit(_In_ T chr)
178 {
179 return '0' <= chr && chr <= '9';
180 }
181
187 template <class T>
188 inline bool isalpha(_In_ T chr)
189 {
190 return islower(chr) || isupper(chr);
191 }
192
198 template <class T>
199 inline bool is7bit(_In_ T chr)
200 {
201 return '\x00' <= chr && chr <= '\x7f';
202 }
203
210 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
211 {
212 _Assume_(glyph || !count);
213 if (count) {
214#ifdef _WIN32
215 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
216#else
217 size_t i = 1;
218#endif
219 for (; i < count && iscombining(glyph[i]); ++i);
220 return i;
221 }
222 return 0;
223 }
224
231 inline size_t glyphrlen(_In_reads_or_z_opt_(count) const wchar_t* str, _In_ size_t count)
232 {
233 _Assume_(count && str && str[count - 1]);
234 for (size_t i = count; i--;) {
235 if (!iscombining(str[i])) {
236#ifdef _WIN32
237 return count - (!is_low_surrogate(str[i]) || i == 0 || !is_high_surrogate(str[i - 1]) ? i : i - 1);
238#else
239 return count - i;
240#endif
241 }
242 }
243 return count;
244 }
245
253 template <class T>
254 inline T tolower(_In_ T chr)
255 {
256 return isupper(chr) ? chr | 0x20 : chr;
257 }
258
266 template <class T>
267 inline T toupper(_In_ T chr)
268 {
269 return islower(chr) ? chr | ~0x20 : chr;
270 }
271
279 template <class T>
280 inline size_t strlen(_In_z_ const T* str)
281 {
282 _Assume_(str);
283 size_t i;
284 for (i = 0; str[i]; ++i);
285 return i;
286 }
287
296 template <class T>
297 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
298 {
299 _Assume_(str || !count);
300 size_t i;
301 for (i = 0; i < count && str[i]; ++i);
302 return i;
303 }
304
312 template <class T, size_t N>
313 inline size_t strnlen(_In_ const T (&str)[N])
314 {
315 return strnlen(str, N);
316 }
317
318 constexpr auto npos{ static_cast<size_t>(-1) };
319
328 template <class T>
329 inline size_t strchr(_In_z_ const T* str, _In_ T chr)
330 {
331 _Assume_(str);
332 for (size_t i = 0; str[i]; ++i)
333 if (str[i] == chr) return i;
334 return npos;
335 }
336
346 template <class T>
347 inline size_t strnchr(
348 _In_reads_or_z_opt_(count) const T* str,
349 _In_ size_t count,
350 _In_ T chr)
351 {
352 _Assume_(str || !count);
353 for (size_t i = 0; i < count && str[i]; ++i)
354 if (str[i] == chr) return i;
355 return npos;
356 }
357
366 template <class T, size_t N>
367 inline size_t strnchr(
368 _In_ const T (&str)[N],
369 _In_ T chr)
370 {
371 return strnchr(str, N, chr);
372 }
373
382 template <class T>
383 inline size_t strrchr(
384 _In_z_ const T* str,
385 _In_ T chr)
386 {
387 _Assume_(str);
388 size_t z = npos;
389 for (size_t i = 0; str[i]; ++i)
390 if (str[i] == chr) z = i;
391 return z;
392 }
393
403 template <class T>
404 inline size_t strrnchr(
405 _In_reads_or_z_opt_(count) const T* str,
406 _In_ size_t count,
407 _In_ T chr)
408 {
409 _Assume_(str || !count);
410 size_t z = npos;
411 for (size_t i = 0; i < count && str[i]; ++i)
412 if (str[i] == chr) z = i;
413 return z;
414 }
415
424 template <class T, size_t N>
425 inline size_t strrnchr(
426 _In_ const T (&str)[N],
427 _In_ T chr)
428 {
429 return strrnchr(str, N, chr);
430 }
431
440 template <class T>
441 inline size_t strichr(
442 _In_z_ const T* str,
443 _In_ T chr)
444 {
445 _Assume_(str);
446 chr = tolower(chr);
447 for (size_t i = 0; str[i]; ++i)
448 if (tolower(str[i]) == chr) return i;
449 return npos;
450 }
451
461 template <class T>
462 inline size_t strichr(
463 _In_z_ const T* str,
464 _In_ T chr,
465 _In_ const std::locale& locale)
466 {
467 _Assume_(str);
468 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
469 chr = ctype.tolower(chr);
470 for (size_t i = 0; str[i]; ++i)
471 if (ctype.tolower(str[i]) == chr) return i;
472 return npos;
473 }
474
484 template <class T>
485 inline size_t strnichr(
486 _In_reads_or_z_opt_(count) const T* str,
487 _In_ size_t count,
488 _In_ T chr)
489 {
490 _Assume_(str || !count);
491 chr = tolower(chr);
492 for (size_t i = 0; i < count && str[i]; ++i)
493 if (tolower(str[i]) == chr) return i;
494 return npos;
495 }
496
507 template <class T>
508 inline size_t strnichr(
509 _In_reads_or_z_opt_(count) const T* str,
510 _In_ size_t count,
511 _In_ T chr,
512 _In_ const std::locale& locale)
513 {
514 _Assume_(str || !count);
515 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
516 chr = ctype.tolower(chr);
517 for (size_t i = 0; i < count && str[i]; ++i)
518 if (ctype.tolower(str[i]) == chr) return i;
519 return npos;
520 }
521
530 template <class T, size_t N>
531 inline size_t strnichr(
532 _In_ const T (&str)[N],
533 _In_ T chr)
534 {
535 return strnichr(str, N, chr);
536 }
537
547 template <class T, size_t N>
548 inline size_t strnichr(
549 _In_ const T (&str)[N],
550 _In_ T chr,
551 _In_ const std::locale& locale)
552 {
553 return strnichr(str, N, chr, locale);
554 }
555
564 template <class T>
565 inline size_t strrichr(
566 _In_z_ const T* str,
567 _In_ T chr)
568 {
569 _Assume_(str);
570 chr = tolower(chr);
571 size_t z = npos;
572 for (size_t i = 0; str[i]; ++i)
573 if (tolower(str[i]) == chr) z = i;
574 return z;
575 }
576
586 template <class T>
587 inline size_t strrichr(
588 _In_reads_or_z_opt_(count) const T* str,
589 _In_ T chr,
590 _In_ const std::locale& locale)
591 {
592 _Assume_(str);
593 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
594 chr = ctype.tolower(chr);
595 size_t z = npos;
596 for (size_t i = 0; str[i]; ++i)
597 if (ctype.tolower(str[i]) == chr) z = i;
598 return z;
599 }
600
610 template <class T>
611 inline size_t strrnichr(
612 _In_reads_or_z_opt_(count) const T* str,
613 _In_ size_t count,
614 _In_ T chr)
615 {
616 _Assume_(str || !count);
617 chr = tolower(chr);
618 size_t z = npos;
619 for (size_t i = 0; i < count && str[i]; ++i)
620 if (tolower(str[i]) == chr) z = i;
621 return z;
622 }
623
634 template <class T>
635 inline size_t strrnichr(
636 _In_reads_or_z_opt_(count) const T* str,
637 _In_ size_t count,
638 _In_ T chr,
639 _In_ const std::locale& locale)
640 {
641 _Assume_(str || !count);
642 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
643 chr = ctype.tolower(chr);
644 size_t z = npos;
645 for (size_t i = 0; i < count && str[i]; ++i)
646 if (ctype.tolower(str[i]) == chr) z = i;
647 return z;
648 }
649
658 template <class T, size_t N>
659 inline size_t strrnichr(
660 _In_ const T (&str)[N],
661 _In_ T chr)
662 {
663 return strrnichr(str, N, chr);
664 }
665
675 template <class T, size_t N>
676 inline size_t strrnichr(
677 _In_ const T (&str)[N],
678 _In_ T chr,
679 _In_ const std::locale& locale)
680 {
681 return strrnichr(str, N, chr, locale);
682 }
683
691 //template <class T>
692 //inline bool isblank(_In_z_ const T* str)
693 //{
694 // _Assume_(str);
695 // for (size_t i = 0; str[i]; ++i)
696 // if (!isspace(str[i]))
697 // return false;
698 // return true;
699 //}
700
709 //template <class T>
710 //inline bool isblank(
711 // _In_z_ const T* str,
712 // _In_ const std::locale& locale)
713 //{
714 // _Assume_(str);
715 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
716 // for (size_t i = 0; str[i]; ++i)
717 // if (!ctype.is(ctype.space, str[i]))
718 // return false;
719 // return true;
720 //}
721
730 template <class T>
731 inline bool isblank(
732 _In_reads_or_z_opt_(count) const T* str,
733 _In_ size_t count)
734 {
735 _Assume_(str || !count);
736 for (size_t i = 0; i < count && str[i]; ++i)
737 if (!isspace(str[i]))
738 return false;
739 return true;
740 }
741
751 template <class T>
752 inline bool isblank(
753 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
754 _In_ const std::locale& locale)
755 {
756 _Assume_(str || !count);
757 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
758 for (size_t i = 0; i < count && str[i]; ++i)
759 if (!ctype.is(ctype.space, str[i]))
760 return false;
761 return true;
762 }
763
771 template <class T, size_t N>
772 inline bool isblank(_In_ const T (&str)[N])
773 {
774 return isblank(str, N);
775 }
776
785 template <class T, size_t N>
786 inline bool isblank(
787 _In_ const T (&str)[N],
788 _In_ const std::locale& locale)
789 {
790 return isblank(str, N, locale);
791 }
792
793 // ///
794 // /// Checks if string contains all-ASCII characters
795 // ///
796 // /// \param[in] str String
797 // ///
798 // /// \return `true` if all characters are ASCII or `false` when any non-ASCII character is found in string.
799 // ///
800 // template <class T>
801 // inline bool is7bit(_In_z_ const T* str)
802 // {
803 // _Assume_(str);
804 // for (size_t i = 0; str[i]; i++)
805 // if (!is7bit(str[i]))
806 // return false;
807 // return true;
808 // }
809
818 template <class T>
819 inline bool is7bit(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
820 {
821 _Assume_(str || !count);
822 for (size_t i = 0; i < count && str[i]; i++)
823 if (!is7bit(str[i]))
824 return false;
825 return true;
826 }
827
835 template <class T, size_t N>
836 inline bool is7bit(_In_ const T (&str)[N])
837 {
838 return is7bit(str, N);
839 }
840
849 template <class T1, class T2>
850 inline int strcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
851 {
852 _Assume_(str1);
853 _Assume_(str2);
854 size_t i; T1 a; T2 b;
855 for (i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
856 if (a > b) return +1;
857 if (a < b) return -1;
858 }
859 if (str1[i]) return +1;
860 if (str2[i]) return -1;
861 return 0;
862 }
863
873 template <class T1, class T2>
874 inline int strncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
875 {
876 _Assume_(str1 || !count);
877 _Assume_(str2 || !count);
878 size_t i; T1 a; T2 b;
879 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
880 if (a > b) return +1;
881 if (a < b) return -1;
882 }
883 if (i < count && str1[i]) return +1;
884 if (i < count && str2[i]) return -1;
885 return 0;
886 }
887
898 template <class T1, class T2>
899 inline int strncmp(
900 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
901 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
902 {
903 _Assume_(str1 || !count1);
904 _Assume_(str2 || !count2);
905 size_t i; T1 a; T2 b;
906 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
907 if (a > b) return +1;
908 if (a < b) return -1;
909 }
910 if (i < count1 && str1[i]) return +1;
911 if (i < count2 && str2[i]) return -1;
912 return 0;
913 }
914
923 template <class T1, size_t N1, class T2, size_t N2>
924 inline int strncmp(
925 _In_ const T1 (&str1)[N1],
926 _In_ const T2 (&str2)[N2])
927 {
928 return strncmp(str1, N1, str2, N2);
929 }
930
939 template <class T1, class T2>
940 inline int strrcmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
941 {
942 size_t
943 i = strlen(str1),
944 j = strlen(str2);
945 _Assume_(str1 || !i);
946 _Assume_(str2 || !j);
947 size_t k; T1 a; T2 b;
948 for (k = 1; i && j; k++) {
949 i--; j--;
950 if ((a = str1[i]) > (b = str2[j])) return +1;
951 if (a < b) return -1;
952 }
953 if (i && !j) return +1;
954 if (!i && j) return -1;
955 return 0;
956 }
957
967 template <class T1, class T2>
968 inline int strrncmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
969 {
970 size_t
971 i = strnlen(str1, count),
972 j = strnlen(str2, count);
973 _Assume_(str1 || !i);
974 _Assume_(str2 || !j);
975 size_t k; T1 a; T2 b;
976 for (k = 1; i && j; k++) {
977 i--; j--;
978 if ((a = str1[i]) > (b = str2[j])) return +1;
979 if (a < b) return -1;
980 }
981 if (i && !j) return +1;
982 if (!i && j) return -1;
983 return 0;
984 }
985
996 template <class T1, class T2>
997 inline int strrncmp(
998 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
999 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1000 {
1001 size_t
1002 i = strnlen(str1, count1),
1003 j = strnlen(str2, count2);
1004 _Assume_(str1 || !i);
1005 _Assume_(str2 || !j);
1006 size_t k; T1 a; T2 b;
1007 for (k = 1; i && j; k++) {
1008 i--; j--;
1009 if ((a = str1[i]) > (b = str2[j])) return +1;
1010 if (a < b) return -1;
1011 }
1012 if (i && !j) return +1;
1013 if (!i && j) return -1;
1014 return 0;
1015 }
1016
1025 template <class T1, size_t N1, class T2, size_t N2>
1026 inline int strrncmp(
1027 _In_ const T1 (&str1)[N1],
1028 _In_ const T2 (&str2)[N2])
1029 {
1030 return strrncmp(str1, N1, str2, N2);
1031 }
1032
1041 template <class T1, class T2>
1042 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
1043 {
1044 _Assume_(str1);
1045 _Assume_(str2);
1046 size_t i; T1 a; T2 b;
1047 for (i = 0; (a = tolower(str1[i])) | (b = tolower(str2[i])); ++i) {
1048 if (a > b) return +1;
1049 if (a < b) return -1;
1050 }
1051 if (str1[i]) return +1;
1052 if (str2[i]) return -1;
1053 return 0;
1054 }
1055
1065 template <class T1, class T2>
1066 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
1067 {
1068 _Assume_(str1);
1069 _Assume_(str2);
1070 size_t i; T1 a; T2 b;
1071 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1072 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1073 for (i = 0; (a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i])); ++i) {
1074 if (a > b) return +1;
1075 if (a < b) return -1;
1076 }
1077 if (str1[i]) return +1;
1078 if (str2[i]) return -1;
1079 return 0;
1080 }
1081
1091 template <class T1, class T2>
1092 inline int strnicmp(_In_reads_or_z_opt_(count) const T1* str1, _In_reads_or_z_opt_(count) const T2* str2, _In_ size_t count)
1093 {
1094 _Assume_(str1 || !count);
1095 _Assume_(str2 || !count);
1096 size_t i; T1 a; T2 b;
1097 for (i = 0; i < count && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); ++i) {
1098 if (a > b) return +1;
1099 if (a < b) return -1;
1100 }
1101 if (i < count && str1[i]) return +1;
1102 if (i < count && str2[i]) return -1;
1103 return 0;
1104 }
1105
1116 template <class T1, class T2>
1117 inline 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)
1118 {
1119 _Assume_(str1 || !count);
1120 _Assume_(str2 || !count);
1121 size_t i; T1 a; T2 b;
1122 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1123 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1124 for (i = 0; i < count && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); ++i) {
1125 if (a > b) return +1;
1126 if (a < b) return -1;
1127 }
1128 if (i < count && str1[i]) return +1;
1129 if (i < count && str2[i]) return -1;
1130 return 0;
1131 }
1132
1143 template <class T1, class T2>
1144 inline int strnicmp(
1145 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1146 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
1147 {
1148 _Assume_(str1 || !count1);
1149 _Assume_(str2 || !count2);
1150 size_t i; T1 a; T2 b;
1151 for (i = 0; i < count1 && i < count2 && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); ++i) {
1152 if (a > b) return +1;
1153 if (a < b) return -1;
1154 }
1155 if (i < count1 && str1[i]) return +1;
1156 if (i < count2 && str2[i]) return -1;
1157 return 0;
1158 }
1159
1171 template <class T1, class T2>
1172 inline int strnicmp(
1173 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
1174 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
1175 _In_ const std::locale& locale)
1176 {
1177 _Assume_(str1 || !count1);
1178 _Assume_(str2 || !count2);
1179 size_t i; T1 a; T2 b;
1180 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1181 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1182 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); ++i) {
1183 if (a > b) return +1;
1184 if (a < b) return -1;
1185 }
1186 if (i < count1 && str1[i]) return +1;
1187 if (i < count2 && str2[i]) return -1;
1188 return 0;
1189 }
1190
1199 template <class T1, size_t N1, class T2, size_t N2>
1200 inline int strnicmp(
1201 _In_ const T1 (&str1)[N1],
1202 _In_ const T2 (&str2)[N2])
1203 {
1204 strnicmp(str1, N1, str2, N2);
1205 }
1206
1216 template <class T1, size_t N1, class T2, size_t N2>
1217 inline int strnicmp(
1218 _In_ const T1 (&str1)[N1],
1219 _In_ const T2 (&str2)[N2],
1220 _In_ const std::locale& locale)
1221 {
1222 strnicmp(str1, N1, str2, N2, locale);
1223 }
1224
1234 template <class T>
1235 inline int strcoll(
1236 _In_z_ const T* str1,
1237 _In_z_ const T* str2,
1238 _In_ const std::locale& locale)
1239 {
1240 _Assume_(str1);
1241 _Assume_(str2);
1242 auto& collate = std::use_facet<std::collate<T>>(locale);
1243 return collate.compare(str1, str1 + strlen(str1), str2, str2 + strlen(str2));
1244 }
1245
1257 template <class T>
1258 inline int strncoll(
1259 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
1260 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
1261 _In_ const std::locale& locale)
1262 {
1263 _Assume_(str1 || !count1);
1264 _Assume_(str2 || !count2);
1265 auto& collate = std::use_facet<std::collate<T>>(locale);
1266 return collate.compare(str1, str1 + count1, str2, str2 + count2);
1267 }
1268
1278 template <class T, size_t N1, size_t N2>
1279 inline int strncoll(
1280 _In_ const T (&str1)[N1],
1281 _In_ const T (&str2)[N2],
1282 _In_ const std::locale& locale)
1283 {
1284 return strncoll(str1, N1, str2, N2, locale);
1285 }
1286
1295 template <class T1, class T2>
1296 inline size_t strstr(
1297 _In_z_ const T1* str,
1298 _In_z_ const T2* sample)
1299 {
1300 _Assume_(str);
1301 _Assume_(sample);
1302 for (size_t offset = 0;; ++offset) {
1303 for (size_t i = offset, j = 0;; ++i, ++j) {
1304 if (!sample[j])
1305 return offset;
1306 if (!str[i])
1307 return npos;
1308 if (str[i] != sample[j])
1309 break;
1310 }
1311 }
1312 }
1313
1323 template <class T1, class T2>
1324 inline size_t strnstr(
1325 _In_reads_or_z_opt_(count) const T1* str, _In_ size_t count,
1326 _In_z_ const T2* sample)
1327 {
1328 _Assume_(str || !count);
1329 _Assume_(sample);
1330 for (size_t offset = 0;; ++offset) {
1331 for (size_t i = offset, j = 0;; ++i, ++j) {
1332 if (!sample[j])
1333 return offset;
1334 if (i >= count || !str[i])
1335 return npos;
1336 if (str[i] != sample[j])
1337 break;
1338 }
1339 }
1340 }
1341
1350 template <class T1, size_t N1, class T2>
1351 inline size_t strnstr(
1352 _In_ const T1 (&str)[N1],
1353 _In_z_ const T2* sample)
1354 {
1355 return strnstr(str, N1, sample);
1356 }
1357
1366 template <class T1, class T2>
1367 inline size_t stristr(
1368 _In_z_ const T1* str,
1369 _In_z_ const T2* sample)
1370 {
1371 _Assume_(str);
1372 _Assume_(sample);
1373 for (size_t offset = 0;; ++offset) {
1374 for (size_t i = offset, j = 0;; ++i, ++j) {
1375 if (!sample[j])
1376 return offset;
1377 if (!str[i])
1378 return npos;
1379 if (tolower(str[i]) != tolower(sample[j]))
1380 break;
1381 }
1382 }
1383 }
1384
1394 template <class T1, class T2>
1395 inline size_t stristr(
1396 _In_z_ const T1* str,
1397 _In_z_ const T2* sample,
1398 _In_ const std::locale& locale)
1399 {
1400 _Assume_(str);
1401 _Assume_(sample);
1402 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1403 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1404 for (size_t offset = 0;; ++offset) {
1405 for (size_t i = offset, j = 0;; ++i, ++j) {
1406 if (!sample[j])
1407 return offset;
1408 if (!str[i])
1409 return npos;
1410 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1411 break;
1412 }
1413 }
1414 }
1415
1425 template <class T1, class T2>
1426 inline size_t strnistr(
1427 _In_reads_or_z_opt_(count) const T1* str,
1428 _In_ size_t count,
1429 _In_z_ const T2* sample)
1430 {
1431 _Assume_(str || !count);
1432 _Assume_(sample);
1433 for (size_t offset = 0;; ++offset) {
1434 for (size_t i = offset, j = 0;; ++i, ++j) {
1435 if (!sample[j])
1436 return offset;
1437 if (i >= count || !str[i])
1438 return npos;
1439 if (tolower(str[i]) != tolower(sample[j]))
1440 break;
1441 }
1442 }
1443 }
1444
1455 template <class T1, class T2>
1456 inline size_t strnistr(
1457 _In_reads_or_z_opt_(count) const T1* str,
1458 _In_ size_t count,
1459 _In_z_ const T2* sample,
1460 _In_ const std::locale& locale)
1461 {
1462 _Assume_(str || !count);
1463 _Assume_(sample);
1464 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
1465 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
1466 for (size_t offset = 0;; ++offset) {
1467 for (size_t i = offset, j = 0;; ++i, ++j) {
1468 if (!sample[j])
1469 return offset;
1470 if (i >= count || !str[i])
1471 return npos;
1472 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
1473 break;
1474 }
1475 }
1476 }
1477
1486 template <class T1, size_t N1, class T2>
1487 inline size_t strnistr(
1488 _In_ const T1 (&str)[N1],
1489 _In_z_ const T2* sample)
1490 {
1491 return strnistr(str, N1, sample);
1492 }
1493
1503 template <class T1, size_t N1, class T2>
1504 inline size_t strnistr(
1505 _In_ const T1 (&str)[N1],
1506 _In_z_ const T2* sample,
1507 _In_ const std::locale& locale)
1508 {
1509 return strnistr(str, N1, sample, locale);
1510 }
1511
1520 template <class T1, class T2>
1521 inline size_t strcpy(
1522 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
1523 _In_z_ const T2* src)
1524 {
1525 _Assume_(dst);
1526 _Assume_(src);
1527 for (size_t i = 0; ; ++i) {
1528 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1529 return i;
1530 }
1531 }
1532
1542 template <class T1, class T2>
1543 inline size_t strncpy(
1544 _Out_writes_(count) _Post_maybez_ T1* dst,
1545 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1546 {
1547 _Assume_(dst || !count);
1548 _Assume_(src || !count);
1549 for (size_t i = 0; ; ++i) {
1550 if (i >= count)
1551 return i;
1552 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1553 return i;
1554 }
1555 }
1556
1567 template <class T1, class T2>
1568 inline size_t strncpy(
1569 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1570 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1571 {
1572 _Assume_(dst || !count_dst);
1573 _Assume_(src || !count_src);
1574 for (size_t i = 0; ; ++i)
1575 {
1576 if (i >= count_dst)
1577 return i;
1578 if (i >= count_src) {
1579 dst[i] = 0;
1580 return i;
1581 }
1582 if ((dst[i] = static_cast<T1>(src[i])) == 0)
1583 return i;
1584 }
1585 }
1586
1595 template <class T1, size_t N1, class T2, size_t N2>
1596 inline size_t strncpy(
1597 _Out_ _Post_maybez_ T1 (&dst)[N1],
1598 _In_ const T2 (&src)[N2])
1599 {
1600 return strncpy(dst, N1, src, N2);
1601 }
1602
1611 template <class T1, class T2>
1612 inline size_t strcat(
1613 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
1614 _In_z_ const T2* src)
1615 {
1616 _Assume_(dst);
1617 _Assume_(src);
1618 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1619 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1620 return j;
1621 }
1622 }
1623
1633 template <class T1, class T2>
1634 inline size_t strncat(
1635 _Inout_z_ T1* dst,
1636 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1637 {
1638 _Assume_(dst || !count);
1639 _Assume_(src || !count);
1640 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1641 if (i >= count)
1642 return j;
1643 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1644 return j;
1645 }
1646 }
1647
1658 template <class T1, class T2>
1659 inline size_t strncat(
1660 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1661 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1662 {
1663 _Assume_(dst || !count_dst);
1664 _Assume_(src || !count_src);
1665 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
1666 {
1667 if (j >= count_dst)
1668 return j;
1669 if (i >= count_src) {
1670 dst[j] = 0;
1671 return j;
1672 }
1673 if ((dst[j] = static_cast<T1>(src[i])) == 0)
1674 return j;
1675 }
1676 }
1677
1688 template <class T>
1689 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
1690 {
1691 if (!str) _Unlikely_
1692 return nullptr;
1693 size_t count = strlen(str) + 1;
1694 T* dst = new T[count];
1695 strncpy(dst, count, str, SIZE_MAX);
1696 return dst;
1697 }
1698
1710 template <class T>
1711 inline _Ret_z_ T* strndup(
1712 _In_reads_or_z_opt_(count) const T* str,
1713 _In_ size_t count)
1714 {
1715 T* dst = new T[count];
1716 strncpy(dst, count, str, SIZE_MAX);
1717 return dst;
1718 }
1719
1730 template <class T, size_t N>
1731 inline _Check_return_ _Ret_maybenull_z_ T* strndup(_In_ const T (&str)[N])
1732 {
1733 return strndup(str, N);
1734 }
1735
1745 template <class T>
1746 inline size_t crlf2nl(_Out_writes_z_(_String_length_(src) + 1) T* dst, _In_z_ const T* src)
1747 {
1748 _Assume_(dst);
1749 _Assume_(src);
1750 size_t i, j;
1751 for (i = j = 0; src[j];) {
1752 if (src[j] != '\r' || src[j + 1] != '\n')
1753 dst[i++] = src[j++];
1754 else {
1755 dst[i++] = '\n';
1756 j += 2;
1757 }
1758 }
1759 dst[i] = 0;
1760 return i;
1761 }
1762
1769 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1770 inline void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& dst, _In_z_ const T* src)
1771 {
1772 _Assume_(src);
1773 _Assume_(src != dst.data());
1774 dst.clear();
1775 dst.reserve(strlen(src));
1776 for (size_t j = 0; src[j];) {
1777 if (src[j] != '\r' || src[j + 1] != '\n')
1778 dst += src[j++];
1779 else {
1780 dst += '\n';
1781 j += 2;
1782 }
1783 }
1784 }
1785
1791 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
1792 inline void crlf2nl(_Inout_ std::basic_string<T, TR, AX>& str)
1793 {
1794 size_t i, j, n;
1795 for (i = j = 0, n = str.size(); j < n;) {
1796 if (str[j] != '\r' || str[j + 1] != '\n')
1797 str[i++] = str[j++];
1798 else {
1799 str[i++] = '\n';
1800 j += 2;
1801 }
1802 }
1803 str.resize(i);
1804 }
1805
1807 template <class T, class T_bin>
1808 inline T_bin strtoint(
1809 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1810 _Out_opt_ size_t* end,
1811 _In_ int radix,
1812 _Out_ uint8_t& flags)
1813 {
1814 _Assume_(str || !count);
1815 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
1816
1817 size_t i = 0;
1818 T_bin value = 0, digit,
1819 max_ui = (T_bin)-1,
1820 max_ui_pre1, max_ui_pre2;
1821
1822 flags = 0;
1823
1824 // Skip leading spaces.
1825 for (;; ++i) {
1826 if (i >= count || !str[i]) goto error;
1827 if (!isspace(str[i])) break;
1828 }
1829
1830 // Read the sign.
1831 if (str[i] == '+') {
1832 flags &= ~0x01;
1833 ++i;
1834 if (i >= count || !str[i]) goto error;
1835 }
1836 else if (str[i] == '-') {
1837 flags |= 0x01;
1838 ++i;
1839 if (i >= count || !str[i]) goto error;
1840 }
1841
1842 if (radix == 16) {
1843 // On hexadecimal, allow leading 0x.
1844 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
1845 i += 2;
1846 if (i >= count || !str[i]) goto error;
1847 }
1848 }
1849 else if (!radix) {
1850 // Autodetect radix.
1851 if (str[i] == '0') {
1852 ++i;
1853 if (i >= count || !str[i]) goto error;
1854 if (str[i] == 'x' || str[i] == 'X') {
1855 radix = 16;
1856 ++i;
1857 if (i >= count || !str[i]) goto error;
1858 }
1859 else
1860 radix = 8;
1861 }
1862 else
1863 radix = 10;
1864 }
1865
1866 // We have the radix.
1867 max_ui_pre1 = max_ui / (T_bin)radix;
1868 max_ui_pre2 = max_ui % (T_bin)radix;
1869 for (;;) {
1870 if ('0' <= str[i] && str[i] <= '9')
1871 digit = (T_bin)str[i] - '0';
1872 else if ('A' <= str[i] && str[i] <= 'Z')
1873 digit = (T_bin)str[i] - 'A' + '\x0a';
1874 else if ('a' <= str[i] && str[i] <= 'z')
1875 digit = (T_bin)str[i] - 'a' + '\x0a';
1876 else
1877 goto error;
1878 if (digit >= (T_bin)radix)
1879 goto error;
1880
1881 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
1882 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
1883 value = value * (T_bin)radix + digit;
1884 else {
1885 // Overflow!
1886 flags |= 0x02;
1887 }
1888
1889 ++i;
1890 if (i >= count || !str[i])
1891 goto error;
1892 }
1893
1894 error:
1895 if (end) *end = i;
1896 return value;
1897 }
1899
1910 template <class T, class T_bin>
1911 T_bin strtoint(
1912 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1913 _Out_opt_ size_t* end,
1914 _In_ int radix)
1915 {
1916 uint8_t flags;
1917 T_bin value;
1918
1919 switch (sizeof(T_bin)) {
1920 case 1:
1921 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
1922 if ((flags & 0x01) && (value & 0x80)) {
1923 // Sign bit is 1 => overflow.
1924 flags |= 0x02;
1925 }
1926 return (flags & 0x02) ?
1927 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
1928 (flags & 0x01) ? -value : value;
1929
1930 case 2:
1931 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
1932 if ((flags & 0x01) && (value & 0x8000)) {
1933 // Sign bit is 1 => overflow.
1934 flags |= 0x02;
1935 }
1936 return (flags & 0x02) ?
1937 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
1938 (flags & 0x01) ? -value : value;
1939
1940 case 4:
1941 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
1942 if ((flags & 0x01) && (value & 0x80000000)) {
1943 // Sign bit is 1 => overflow.
1944 flags |= 0x02;
1945 }
1946 return (flags & 0x02) ?
1947 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
1948 (flags & 0x01) ? -value : value;
1949
1950 case 8:
1951 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
1952 if ((flags & 0x01) && (value & 0x8000000000000000)) {
1953 // Sign bit is 1 => overflow.
1954 flags |= 0x02;
1955 }
1956 return (flags & 0x02) ?
1957 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
1958 (flags & 0x01) ? -value : value;
1959
1960 default:
1961 throw std::invalid_argument("Unsupported bit length");
1962 }
1963 }
1964
1974 template <class T, size_t N, class T_bin>
1975 T_bin strtoint(
1976 _In_ const T (&str)[N],
1977 _Out_opt_ size_t* end,
1978 _In_ int radix)
1979 {
1980 return strtoint<T, T_bin>(str, N, end, radix);
1981 }
1982
1993 template <class T, class T_bin>
1994 inline T_bin strtouint(
1995 _In_reads_or_z_opt_(count) const T* str,
1996 _In_ size_t count,
1997 _Out_opt_ size_t* end,
1998 _In_ int radix)
1999 {
2000 uint8_t flags;
2001 T_bin value;
2002
2003 switch (sizeof(T_bin)) {
2004 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
2005 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
2006 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
2007 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
2008 default: throw std::invalid_argument("Unsupported bit length");
2009 }
2010
2011 return (flags & 0x02) ?
2012 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
2013 (flags & 0x01) ? ~value : value;
2014 }
2015
2025 template <class T, size_t N, class T_bin>
2026 inline T_bin strtouint(
2027 _In_ const T (&str)[N],
2028 _Out_opt_ size_t* end,
2029 _In_ int radix)
2030 {
2031 return strtouint<T, T_bin>(str, N, end, radix);
2032 }
2033
2044 template <class T>
2045 inline int32_t strto32(
2046 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2047 _Out_opt_ size_t* end,
2048 _In_ int radix)
2049 {
2050 return strtoint<T, int32_t>(str, count, end, radix);
2051 }
2052
2062 template <class T, size_t N>
2063 inline int32_t strto32(
2064 _In_ const T (&str)[N],
2065 _Out_opt_ size_t* end,
2066 _In_ int radix)
2067 {
2068 return strto32<T>(str, N, end, radix);
2069 }
2070
2081 template <class T>
2082 inline int64_t strto64(
2083 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2084 _Out_opt_ size_t* end,
2085 _In_ int radix)
2086 {
2087 return strtoint<T, int64_t>(str, count, end, radix);
2088 }
2089
2099 template <class T, size_t N>
2100 inline int64_t strto64(
2101 _In_ const T (&str)[N],
2102 _Out_opt_ size_t* end,
2103 _In_ int radix)
2104 {
2105 return strto64<T>(str, N, end, radix);
2106 }
2107
2119 template <class T>
2120 inline ptrdiff_t strtoi(
2121 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2122 _Out_opt_ size_t* end,
2123 _In_ int radix)
2124 {
2125#if defined(_WIN64) || defined(__LP64__)
2126 return static_cast<ptrdiff_t>(strto64(str, count, end, radix));
2127#else
2128 return static_cast<ptrdiff_t>(strto32(str, count, end, radix));
2129#endif
2130 }
2131
2142 template <class T, size_t N>
2143 inline ptrdiff_t strtoi(
2144 _In_ const T (&str)[N],
2145 _Out_opt_ size_t* end,
2146 _In_ int radix)
2147 {
2148 return strtoi<T>(str, N, end, radix);
2149 }
2150
2161 template <class T>
2162 inline uint32_t strtou32(
2163 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2164 _Out_opt_ size_t* end,
2165 _In_ int radix)
2166 {
2167 return strtouint<T, uint32_t>(str, count, end, radix);
2168 }
2169
2179 template <class T, size_t N>
2180 inline uint32_t strtou32(
2181 _In_ const T (&str)[N],
2182 _Out_opt_ size_t* end,
2183 _In_ int radix)
2184 {
2185 return strtou32(str, N, end, radix);
2186 }
2187
2198 template <class T>
2199 inline uint64_t strtou64(
2200 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2201 _Out_opt_ size_t* end,
2202 _In_ int radix)
2203 {
2204 return strtouint<T, uint64_t>(str, count, end, radix);
2205 }
2206
2216 template <class T, size_t N>
2217 inline uint64_t strtou64(
2218 _In_ const T (&str)[N],
2219 _Out_opt_ size_t* end,
2220 _In_ int radix)
2221 {
2222 return strtou64<T>(str, N, end, radix);
2223 }
2224
2236 template <class T>
2237 inline size_t strtoui(
2238 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
2239 _Out_opt_ size_t* end,
2240 _In_ int radix)
2241 {
2242#if defined(_WIN64) || defined(__LP64__)
2243 return static_cast<size_t>(strtou64(str, count, end, radix));
2244#else
2245 return static_cast<size_t>(strtou32(str, count, end, radix));
2246#endif
2247 }
2248
2259 template <class T, size_t N>
2260 inline size_t strtoui(
2261 _In_ const T (&str)[N],
2262 _Out_opt_ size_t* end,
2263 _In_ int radix)
2264 {
2265 return strtoui<T>(str, N, end, radix);
2266 }
2267
2278 inline double strtod(
2279 _In_reads_or_z_opt_(count) const char* str, _In_ size_t count,
2280 _Out_opt_ size_t* end,
2281 _In_opt_ locale_t locale)
2282 {
2283 count = strnlen(str, count);
2284 _Assume_(str || !count);
2285 std::string tmp(str, count);
2286 char* _end;
2287 double r;
2288#if _WIN32
2289 r = _strtod_l(tmp.c_str(), &_end, locale);
2290#else
2291 r = strtod_l(tmp.c_str(), &_end, locale);
2292#endif
2293 if (end) *end = (size_t)(_end - tmp.c_str());
2294 return r;
2295 }
2296
2307 inline double strtod(
2308 _In_reads_or_z_opt_(count) const wchar_t* str, _In_ size_t count,
2309 _Out_opt_ size_t* end,
2310 _In_opt_ locale_t locale)
2311 {
2312 count = strnlen(str, count);
2313 _Assume_(str || !count);
2314 std::wstring tmp(str, count);
2315 wchar_t* _end;
2316 double r;
2317#if _WIN32
2318 r = _wcstod_l(tmp.c_str(), &_end, locale);
2319#else
2320 r = wcstod_l(tmp.c_str(), &_end, locale);
2321#endif
2322 if (end) *end = (size_t)(_end - tmp.c_str());
2323 return r;
2324 }
2325
2327 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)
2328 {
2329#ifdef _WIN32
2330#pragma warning(suppress: 4996)
2331 return _vsnprintf_l(str, capacity, format, locale, arg);
2332#else
2333 va_list arg_mutable;
2334 va_copy(arg_mutable, arg);
2335 return ::vsnprintf_l(str, capacity, locale, format, arg_mutable);
2336#endif
2337 }
2338
2339 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)
2340 {
2341#ifdef _WIN32
2342#pragma warning(suppress: 4996)
2343 return _vsnwprintf_l(str, capacity, format, locale, arg);
2344#else
2345 va_list arg_mutable;
2346 va_copy(arg_mutable, arg);
2347 return ::vswprintf_l(str, capacity, locale, format, arg_mutable);
2348#endif
2349 }
2351
2362 template<class T, class TR, class AX>
2363 inline 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)
2364 {
2365 T buf[1024 / sizeof(T)];
2366
2367 // Try with stack buffer first.
2368 int count = vsnprintf(buf, _countof(buf), format, locale, arg);
2369 if (0 <= count && static_cast<size_t>(count) <= _countof(buf)) {
2370 // Copy from stack.
2371 str.append(buf, static_cast<size_t>(count));
2372 return static_cast<size_t>(count);
2373 }
2374#ifdef _WIN32
2375 if (count < 0) {
2376 switch (errno) {
2377 case 0:
2378 count = vsnprintf(NULL, 0, format, locale, arg);
2379 _Assume_(count >= 0);
2380 break;
2381 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2382 case EILSEQ: throw std::runtime_error("encoding error");
2383 default: throw std::runtime_error("failed to format string");
2384 }
2385 }
2386 size_t offset = str.size();
2387 str.resize(offset + count);
2388 if (vsnprintf(&str[offset], count + 1, format, locale, arg) != count) _Unlikely_
2389 throw std::runtime_error("failed to format string");
2390#else
2391 size_t offset = str.size();
2392 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2393 switch (errno) {
2394 case EOVERFLOW:
2395 // Allocate on heap and retry.
2396 str.resize(offset + capacity);
2397 count = vsnprintf(&str[offset], capacity, format, locale, arg);
2398 if (0 <= count && static_cast<size_t>(count) <= capacity) {
2399 str.resize(offset + static_cast<size_t>(count));
2400 return static_cast<size_t>(count);
2401 }
2402 break;
2403 case EINVAL: throw std::invalid_argument("invalid vsnprintf arguments");
2404 case EILSEQ: throw std::runtime_error("encoding error");
2405 default: throw std::runtime_error("failed to format string");
2406 }
2407 }
2408#endif
2409 return static_cast<size_t>(count);
2410 }
2411
2421 template<class T, class TR, class AX>
2422 inline 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, ...)
2423 {
2424 va_list arg;
2425 va_start(arg, locale);
2426 size_t n = vappendf(str, format, locale, arg);
2427 va_end(arg);
2428 return n;
2429 }
2430
2439 template<class T, class TR, class AX>
2440 inline 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)
2441 {
2442 str.clear();
2443 vappendf(str, format, locale, arg);
2444 }
2445
2453 template<class T, class TR, class AX>
2454 inline 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, ...)
2455 {
2456 va_list arg;
2457 va_start(arg, locale);
2458 vsprintf(str, format, locale, arg);
2459 va_end(arg);
2460 }
2461
2471 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2472 inline 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)
2473 {
2474 std::basic_string<T, TR, AX> str;
2475 vappendf(str, format, locale, arg);
2476 return str;
2477 }
2478
2487 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2488 inline std::basic_string<T, TR, AX> sprintf(_In_z_ _Printf_format_string_params_(2) const T* format, _In_opt_ locale_t locale, ...)
2489 {
2490 va_list arg;
2491 va_start(arg, locale);
2492 auto str = vsprintf(format, locale, arg);
2493 va_end(arg);
2494 return str;
2495 }
2496
2498 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)
2499 {
2500#ifdef _WIN32
2501 return _strftime_l(str, capacity, format, time, locale);
2502#else
2503 return strftime_l(str, capacity, format, time, locale);
2504#endif
2505 }
2506
2507 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)
2508 {
2509#ifdef _WIN32
2510 return _wcsftime_l(str, capacity, format, time, locale);
2511#else
2512 return wcsftime_l(str, capacity, format, time, locale);
2513#endif
2514 }
2516
2525 template<class T, class TR, class AX>
2526 inline 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)
2527 {
2528 T buf[1024 / sizeof(T)];
2529
2530 // Try with stack buffer first.
2531 size_t count = strftime(buf, _countof(buf), format, time, locale);
2532 if (count) {
2533 // Copy from stack.
2534 str.append(buf, count);
2535 return;
2536 }
2537 size_t offset = str.size();
2538 for (size_t capacity = 2 * 1024 / sizeof(T);; capacity *= 2) {
2539 // Allocate on heap and retry.
2540 str.resize(offset + capacity);
2541 count = strftime(&str[offset], capacity + 1, format, time, locale);
2542 if (count) {
2543 str.resize(offset + count);
2544 return;
2545 }
2546 }
2547 }
2548
2557 template<class T, class TR, class AX>
2558 inline 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)
2559 {
2560 str.clear();
2561 strcatftime(str, format, time, locale);
2562 }
2563
2573 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2574 inline 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)
2575 {
2576 std::basic_string<T, TR, AX> str;
2577 strcatftime(str, format, time, locale);
2578 return str;
2579 }
2580
2586 //template<class T>
2587 //inline void strlwr(_Inout_z_ T* str)
2588 //{
2589 // _Assume_(str);
2590 // for (size_t i = 0; str[i]; ++i)
2591 // str[i] = tolower(str[i]);
2592 //}
2593
2600 //template<class T>
2601 //inline void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
2602 //{
2603 // _Assume_(str);
2604 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2605 // for (size_t i = 0; str[i]; ++i)
2606 // str[i] = ctype.tolower(str[i]);
2607 //}
2608
2615 template<class T>
2616 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2617 {
2618 _Assume_(str || !count);
2619 for (size_t i = 0; i < count && str[i]; ++i)
2620 str[i] = tolower(str[i]);
2621 }
2622
2630 template<class T>
2631 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2632 {
2633 _Assume_(str || !count);
2634 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2635 for (size_t i = 0; i < count && str[i]; ++i)
2636 str[i] = ctype.tolower(str[i]);
2637 }
2638
2644 template<class T, size_t N>
2645 inline void strlwr(_Inout_ T (&str)[N])
2646 {
2647 strlwr(str, N);
2648 }
2649
2656 template<class T, size_t N>
2657 inline void strlwr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2658 {
2659 strlwr(str, N, locale);
2660 }
2661
2667 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2668 inline void strlwr(_Inout_ std::basic_string<T, TR, AX>& str)
2669 {
2670 for (auto& c : str)
2671 c = tolower(c);
2672 }
2673
2680 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2681 inline void strlwr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2682 {
2683 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2684 for (auto& c : str)
2685 c = ctype.tolower(c);
2686 }
2687
2693 //template<class T>
2694 //inline void strupr(_Inout_z_ T* str)
2695 //{
2696 // _Assume_(str);
2697 // for (size_t i = 0; str[i]; ++i)
2698 // str[i] = toupper(str[i]);
2699 //}
2700
2707 //template<class T>
2708 //inline void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
2709 //{
2710 // _Assume_(str);
2711 // const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2712 // for (size_t i = 0; str[i]; ++i)
2713 // str[i] = ctype.toupper(str[i]);
2714 //}
2715
2722 template<class T>
2723 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count)
2724 {
2725 _Assume_(str || !count);
2726 for (size_t i = 0; i < count && str[i]; ++i)
2727 str[i] = toupper(str[i]);
2728 }
2729
2737 template<class T>
2738 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
2739 {
2740 _Assume_(str || !count);
2741 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2742 for (size_t i = 0; i < count && str[i]; ++i)
2743 str[i] = ctype.toupper(str[i]);
2744 }
2745
2751 template<class T, size_t N>
2752 inline void strupr(_Inout_ T (&str)[N])
2753 {
2754 return strupr(str, N);
2755 }
2756
2763 template<class T, size_t N>
2764 inline void strupr(_Inout_ T (&str)[N], _In_ const std::locale& locale)
2765 {
2766 return strupr(str, N, locale);
2767 }
2768
2774 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2775 inline void strupr(_Inout_ std::basic_string<T, TR, AX>& str)
2776 {
2777 for (auto& c : str)
2778 c = toupper(c);
2779 }
2780
2787 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2788 inline void strupr(_Inout_ std::basic_string<T, TR, AX>& str, _In_ const std::locale& locale)
2789 {
2790 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2791 for (auto& c : str)
2792 c = ctype.toupper(c);
2793 }
2794
2803 template<class T>
2804 inline size_t ltrim(
2805 _Inout_z_count_(count) T* str, _In_ size_t count)
2806 {
2807 for (size_t i = 0;; ++i) {
2808 if (i >= count) {
2809 if (count) str[0] = 0;
2810 return 0;
2811 }
2812 if (!str[i]) {
2813 str[0] = 0;
2814 return 0;
2815 }
2816 if (!isspace(str[i])) {
2817 if (!i)
2818 return strnlen(str, count);
2819 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2820 str[n] = 0;
2821 return n;
2822 }
2823 }
2824 }
2825
2835 template<class T>
2836 inline size_t ltrim(
2837 _Inout_z_count_(count) T* str, _In_ size_t count,
2838 _In_ const std::locale& locale)
2839 {
2840 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2841 for (size_t i = 0;; ++i) {
2842 if (i >= count) {
2843 if (count) str[0] = 0;
2844 return 0;
2845 }
2846 if (!str[i]) {
2847 str[0] = 0;
2848 return 0;
2849 }
2850 if (!ctype.is(ctype.space, str[i])) {
2851 if (!i)
2852 return strnlen(str, count);
2853 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2854 str[n] = 0;
2855 return n;
2856 }
2857 }
2858 }
2859
2865 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2866 inline void ltrim(_Inout_ std::basic_string<T, TR, AX>& s)
2867 {
2868 s.erase(
2869 s.begin(),
2870 std::find_if(
2871 s.begin(),
2872 s.end(),
2873 [&](_In_ T ch) { return !isspace(ch); }));
2874 }
2875
2882 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2883 inline void ltrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
2884 {
2885 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2886 s.erase(
2887 s.begin(),
2888 std::find_if(
2889 s.begin(),
2890 s.end(),
2891 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }));
2892 }
2893
2902 template<class T>
2903 inline size_t rtrim(
2904 _Inout_z_count_(count) T* str, _In_ size_t count)
2905 {
2906 for (size_t i = 0, j = 0;;) {
2907 if (i >= count || !str[i]) {
2908 if (j < count) str[j] = 0;
2909 return j;
2910 }
2911 if (!isspace(str[i]))
2912 j = ++i;
2913 else
2914 ++i;
2915 }
2916 }
2917
2927 template<class T>
2928 inline size_t rtrim(
2929 _Inout_z_count_(count) T* str, _In_ size_t count,
2930 _In_ const std::locale& locale)
2931 {
2932 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2933 for (size_t i = 0, j = 0;;) {
2934 if (i >= count || !str[i]) {
2935 if (j < count) str[j] = 0;
2936 return j;
2937 }
2938 if (!ctype.is(ctype.space, str[i]))
2939 j = ++i;
2940 else
2941 ++i;
2942 }
2943 }
2944
2950 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2951 static inline void rtrim(_Inout_ std::basic_string<T, TR, AX>& s)
2952 {
2953 s.erase(
2954 std::find_if(
2955 s.rbegin(),
2956 s.rend(),
2957 [&](_In_ T ch) { return !isspace(ch); }).base(),
2958 s.end());
2959 }
2960
2967 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
2968 static inline void rtrim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
2969 {
2970 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2971 s.erase(
2972 std::find_if(
2973 s.rbegin(),
2974 s.rend(),
2975 [&](_In_ T ch) { return !ctype.is(ctype.space, ch); }).base(),
2976 s.end());
2977 }
2978
2987 template<class T>
2988 inline size_t trim(
2989 _Inout_z_count_(count) T* str, _In_ size_t count)
2990 {
2991 return ltrim(str, rtrim(str, count));
2992 }
2993
3003 template<class T>
3004 inline size_t trim(
3005 _Inout_z_count_(count) T* str, _In_ size_t count,
3006 _In_ const std::locale& locale)
3007 {
3008 return ltrim(str, rtrim(str, count, locale), locale);
3009 }
3010
3016 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3017 static inline void trim(_Inout_ std::basic_string<T, TR, AX>& s)
3018 {
3019 auto nonspace = [&](_In_ T ch) { return !isspace(ch); };
3020 s.erase(
3021 s.begin(),
3022 std::find_if(
3023 s.begin(),
3024 s.end(),
3025 nonspace));
3026 s.erase(
3027 std::find_if(
3028 s.rbegin(),
3029 s.rend(),
3030 nonspace).base(),
3031 s.end());
3032 }
3033
3040 template<class T, class TR = std::char_traits<T>, class AX = std::allocator<T>>
3041 static inline void trim(_Inout_ std::basic_string<T, TR, AX>& s, _In_ const std::locale& locale)
3042 {
3043 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
3044 auto nonspace = [&](_In_ T ch) { return !ctype.is(ctype.space, ch); };
3045 s.erase(
3046 s.begin(),
3047 std::find_if(
3048 s.begin(),
3049 s.end(),
3050 nonspace));
3051 s.erase(
3052 std::find_if(
3053 s.rbegin(),
3054 s.rend(),
3055 nonspace).base(),
3056 s.end());
3057 }
3058}