stdex
Additional custom or not Standard C++ covered algorithms
Loading...
Searching...
No Matches
string.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2016-2023 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 typedef wchar_t utf16_t;
31#else
32 typedef char16_t utf16_t;
33#endif
34
40 inline bool is_high_surrogate(_In_ utf16_t chr)
41 {
42 return 0xd800 < chr && chr < 0xdc00;
43 }
44
50 inline bool is_low_surrogate(_In_ utf16_t chr)
51 {
52 return 0xdc00 < chr && chr < 0xe000;
53 }
54
60 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
61 {
62 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
63 }
64
70 inline char32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
71 {
72 _Assume_(is_surrogate_pair(str));
73 return
74 ((char32_t)(str[0] - 0xd800) << 10) +
75 (char32_t)(str[1] - 0xdc00) +
76 0x10000;
77 }
78
84 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ char32_t chr)
85 {
86 _Assume_(chr >= 0x10000);
87 chr -= 0x10000;
88 str[0] = 0xd800 + (char32_t)((chr >> 10) & 0x3ff);
89 str[1] = 0xdc00 + (char32_t)(chr & 0x3ff);
90 }
91
97 inline bool iscombining(_In_ char32_t chr)
98 {
99 return
100 (0x0300 <= chr && chr < 0x0370) ||
101 (0x1dc0 <= chr && chr < 0x1e00) ||
102 (0x20d0 <= chr && chr < 0x2100) ||
103 (0xfe20 <= chr && chr < 0xfe30);
104 }
105
111 template <class T>
112 inline size_t islbreak(_In_ T chr)
113 {
114 return chr == '\n' || chr == '\r';
115 }
116
123 template <class T>
124 inline size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
125 {
126 _Assume_(chr || !count);
127 if (count >= 2 && ((chr[0] == '\r' && chr[1] == '\n') || (chr[0] == '\n' && chr[1] == '\r')))
128 return 2;
129 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
130 return 1;
131 return 0;
132 }
133
140 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
141 {
142 _Assume_(glyph || !count);
143 if (count) {
144#ifdef _WIN32
145 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
146#else
147 size_t i = 1;
148#endif
149 for (; i < count && iscombining(glyph[i]); ++i);
150 return i;
151 }
152 return 0;
153 }
154
162 template <class T>
163 inline size_t strlen(_In_z_ const T* str)
164 {
165 _Assume_(str);
166 size_t i;
167 for (i = 0; str[i]; ++i);
168 return i;
169 }
170
179 template <class T>
180 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
181 {
182 _Assume_(str || !count);
183 size_t i;
184 for (i = 0; i < count && str[i]; ++i);
185 return i;
186 }
187
188 constexpr auto npos{ static_cast<size_t>(-1) };
189
198 template <class T>
199 inline size_t strchr(_In_z_ const T* str, _In_ T chr)
200 {
201 _Assume_(str);
202 for (size_t i = 0; str[i]; ++i)
203 if (str[i] == chr) return i;
204 return npos;
205 }
206
216 template <class T>
217 inline size_t strnchr(
218 _In_reads_or_z_opt_(count) const T* str,
219 _In_ size_t count,
220 _In_ T chr)
221 {
222 _Assume_(str || !count);
223 for (size_t i = 0; i < count && str[i]; ++i)
224 if (str[i] == chr) return i;
225 return npos;
226 }
227
237 template <class T>
238 inline size_t strrnchr(
239 _In_reads_or_z_opt_(count) const T* str,
240 _In_ size_t count,
241 _In_ T chr)
242 {
243 _Assume_(str || !count);
244 size_t z = npos;
245 for (size_t i = 0; i < count && str[i]; ++i)
246 if (str[i] == chr) z = i;
247 return z;
248 }
249
259 template <class T>
260 inline size_t strnichr(
261 _In_reads_or_z_opt_(count) const T* str,
262 _In_ size_t count,
263 _In_ T chr,
264 _In_ const std::locale& locale)
265 {
266 _Assume_(str || !count);
267 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
268 chr = ctype.tolower(chr);
269 for (size_t i = 0; i < count && str[i]; ++i)
270 if (ctype.tolower(str[i]) == chr) return i;
271 return npos;
272 }
273
283 template <class T>
284 inline size_t strrnichr(
285 _In_reads_or_z_opt_(count) const T* str,
286 _In_ size_t count,
287 _In_ T chr,
288 _In_ const std::locale& locale)
289 {
290 _Assume_(str || !count);
291 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
292 chr = ctype.tolower(chr);
293 size_t z = npos;
294 for (size_t i = 0; i < count && str[i]; ++i)
295 if (ctype.tolower(str[i]) == chr) z = i;
296 return z;
297 }
298
307 template <class T1, class T2>
308 inline int strcmp(const T1* str1, const T2* str2)
309 {
310 _Assume_(str1 && str2);
311 T1 a; T2 b;
312 for (size_t i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
313 if (a > b) return +1;
314 if (a < b) return -1;
315 }
316 return 0;
317 }
318
329 template <class T1, class T2>
330 inline int strncmp(
331 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
332 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
333 {
334 _Assume_(str1 || !count1);
335 _Assume_(str2 || !count2);
336 size_t i; T1 a; T2 b;
337 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
338 if (a > b) return +1;
339 if (a < b) return -1;
340 }
341 if (i < count1 && str1[i]) return +1;
342 if (i < count2 && str2[i]) return -1;
343 return 0;
344 }
345
355 template <class T1, class T2>
356 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)
357 {
358 _Assume_((str1 && str2) || !count);
359 size_t i; T1 a; T2 b;
360 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
361 if (a > b) return +1;
362 if (a < b) return -1;
363 }
364 if (i < count && str1[i]) return +1;
365 if (i < count && str2[i]) return -1;
366 return 0;
367 }
368
379 template <class T>
380 inline int strncoll(
381 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
382 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
383 _In_ const std::locale& locale)
384 {
385 _Assume_(str1 || !count1);
386 _Assume_(str2 || !count2);
387 auto& collate = std::use_facet<std::collate<T>>(locale);
388 return collate.compare(str1, str1 + count1, str2, str2 + count2);
389 }
390
399 template <class T1, class T2>
400 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
401 {
402 _Assume_(str1);
403 _Assume_(str2);
404 size_t i; T1 a; T2 b;
405 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
406 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
407 for (i = 0; (a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i])); i++) {
408 if (a > b) return +1;
409 if (a < b) return -1;
410 }
411 if (str1[i]) return +1;
412 if (str2[i]) return -1;
413 return 0;
414 }
415
425 template <class T1, class T2>
426 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)
427 {
428 _Assume_(str1 || !count);
429 _Assume_(str2 || !count);
430 size_t i; T1 a; T2 b;
431 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
432 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
433 for (i = 0; i < count && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
434 if (a > b) return +1;
435 if (a < b) return -1;
436 }
437 if (i < count && str1[i]) return +1;
438 if (i < count && str2[i]) return -1;
439 return 0;
440 }
441
452 template <class T1, class T2>
453 inline int strnicmp(
454 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
455 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
456 _In_ const std::locale& locale)
457 {
458 _Assume_(str1 || !count1);
459 _Assume_(str2 || !count2);
460 size_t i; T1 a; T2 b;
461 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
462 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
463 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
464 if (a > b) return +1;
465 if (a < b) return -1;
466 }
467 if (i < count1 && str1[i]) return +1;
468 if (i < count2 && str2[i]) return -1;
469 return 0;
470 }
471
480 template <class T1, class T2>
481 inline size_t strstr(
482 _In_z_ const T1* str,
483 _In_z_ const T2* sample)
484 {
485 _Assume_(str);
486 _Assume_(sample);
487 for (size_t offset = 0;; ++offset) {
488 for (size_t i = offset, j = 0;; ++i, ++j) {
489 if (!sample[j])
490 return offset;
491 if (!str[i])
492 return npos;
493 if (str[i] != sample[j])
494 break;
495 }
496 }
497 }
498
508 template <class T1, class T2>
509 inline size_t strnstr(
510 _In_reads_or_z_opt_(count) const T1* str,
511 _In_ size_t count,
512 _In_z_ const T2* sample)
513 {
514 _Assume_(str || !count);
515 _Assume_(sample);
516 for (size_t offset = 0;; ++offset) {
517 for (size_t i = offset, j = 0;; ++i, ++j) {
518 if (!sample[j])
519 return offset;
520 if (i >= count || !str[i])
521 return npos;
522 if (str[i] != sample[j])
523 break;
524 }
525 }
526 }
527
536 template <class T1, class T2>
537 inline size_t stristr(
538 _In_z_ const T1* str,
539 _In_z_ const T2* sample,
540 _In_ const std::locale& locale)
541 {
542 _Assume_(str);
543 _Assume_(sample);
544 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
545 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
546 for (size_t offset = 0;; ++offset) {
547 for (size_t i = offset, j = 0;; ++i, ++j) {
548 if (!sample[j])
549 return offset;
550 if (!str[i])
551 return npos;
552 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
553 break;
554 }
555 }
556 }
557
567 template <class T1, class T2>
568 inline size_t strnistr(
569 _In_reads_or_z_opt_(count) const T1* str,
570 _In_ size_t count,
571 _In_z_ const T2* sample,
572 _In_ const std::locale& locale)
573 {
574 _Assume_(str || !count);
575 _Assume_(sample);
576 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
577 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
578 for (size_t offset = 0;; ++offset) {
579 for (size_t i = offset, j = 0;; ++i, ++j) {
580 if (!sample[j])
581 return offset;
582 if (i >= count || !str[i])
583 return npos;
584 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
585 break;
586 }
587 }
588 }
589
598 template <class T1, class T2>
599 inline size_t strcpy(
600 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
601 _In_z_ const T2* src)
602 {
603 _Assume_(dst && src);
604 for (size_t i = 0; ; ++i) {
605 if ((dst[i] = src[i]) == 0)
606 return i;
607 }
608 }
609
619 template <class T1, class T2>
620 inline size_t strncpy(
621 _Out_writes_(count) _Post_maybez_ T1* dst,
622 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
623 {
624 _Assume_(dst && src || !count);
625 for (size_t i = 0; ; ++i) {
626 if (i >= count)
627 return i;
628 if ((dst[i] = src[i]) == 0)
629 return i;
630 }
631 }
632
643 template <class T1, class T2>
644 inline size_t strncpy(
645 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
646 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
647 {
648 _Assume_(dst || !count_dst);
649 _Assume_(src || !count_src);
650 for (size_t i = 0; ; ++i)
651 {
652 if (i >= count_dst)
653 return i;
654 if (i >= count_src) {
655 dst[i] = 0;
656 return i;
657 }
658 if ((dst[i] = src[i]) == 0)
659 return i;
660 }
661 }
662
671 template <class T1, class T2>
672 inline size_t strcat(
673 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
674 _In_z_ const T2* src)
675 {
676 _Assume_(dst && src);
677 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
678 if ((dst[j] = src[i]) == 0)
679 return j;
680 }
681 }
682
692 template <class T1, class T2>
693 inline size_t strncat(
694 _Inout_z_ T1* dst,
695 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
696 {
697 _Assume_(dst && src || !count);
698 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
699 if (i >= count)
700 return j;
701 if ((dst[j] = src[i]) == 0)
702 return j;
703 }
704 }
705
716 template <class T1, class T2>
717 inline size_t strncat(
718 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
719 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
720 {
721 _Assume_(dst || !count_dst);
722 _Assume_(src || !count_src);
723 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
724 {
725 if (j >= count_dst)
726 return j;
727 if (i >= count_src) {
728 dst[j] = 0;
729 return j;
730 }
731 if ((dst[j] = src[i]) == 0)
732 return j;
733 }
734 }
735
746 template <class T>
747 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
748 {
749 if (!str) _Unlikely_
750 return nullptr;
751 size_t count = strlen(str) + 1;
752 T* dst = new T[count];
753 strncpy(dst, count, str, SIZE_MAX);
754 return dst;
755 }
756
768 template <class T>
769 inline _Ret_z_ T* strndup(
770 _In_reads_or_z_opt_(count) const T* str,
771 _In_ size_t count)
772 {
773 T* dst = new T[count];
774 strncpy(dst, count, str, SIZE_MAX);
775 return dst;
776 }
777
787 template <class T>
788 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
789 {
790 _Assume_(dst);
791 _Assume_(src);
792 size_t i, j;
793 for (i = j = 0; src[j];) {
794 if (src[j] != '\r' || src[j + 1] != '\n')
795 dst[i++] = src[j++];
796 else {
797 dst[i++] = '\n';
798 j += 2;
799 }
800 }
801 dst[i] = 0;
802 return i;
803 }
804
811 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
812 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &dst, _In_z_ const _Elem* src)
813 {
814 _Assume_(src);
815 _Assume_(src != dst.c_str());
816 dst.clear();
817 dst.reserve(strlen(src));
818 for (size_t j = 0; src[j];) {
819 if (src[j] != '\r' || src[j + 1] != '\n')
820 dst += src[j++];
821 else {
822 dst += '\n';
823 j += 2;
824 }
825 }
826 }
827
833 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
834 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
835 {
836 size_t i, j, n;
837 for (i = j = 0, n = str.size(); j < n;) {
838 if (str[j] != '\r' || str[j + 1] != '\n')
839 str[i++] = str[j++];
840 else {
841 str[i++] = '\n';
842 j += 2;
843 }
844 }
845 str.resize(i);
846 }
847
849 template <class T, class T_bin>
850 inline T_bin strtoint(
851 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
852 _Out_opt_ size_t* end,
853 _In_ int radix,
854 _Out_ uint8_t& flags)
855 {
856 _Assume_(str || !count);
857 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
858
859 size_t i = 0;
860 T_bin value = 0, digit,
861 max_ui = (T_bin)-1,
862 max_ui_pre1, max_ui_pre2;
863
864 flags = 0;
865
866 // Skip leading spaces.
867 for (;; ++i) {
868 if (i >= count || !str[i]) goto error;
869 if (!isspace(str[i])) break;
870 }
871
872 // Read the sign.
873 if (str[i] == '+') {
874 flags &= ~0x01;
875 ++i;
876 if (i >= count || !str[i]) goto error;
877 }
878 else if (str[i] == '-') {
879 flags |= 0x01;
880 ++i;
881 if (i >= count || !str[i]) goto error;
882 }
883
884 if (radix == 16) {
885 // On hexadecimal, allow leading 0x.
886 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
887 i += 2;
888 if (i >= count || !str[i]) goto error;
889 }
890 }
891 else if (!radix) {
892 // Autodetect radix.
893 if (str[i] == '0') {
894 ++i;
895 if (i >= count || !str[i]) goto error;
896 if (str[i] == 'x' || str[i] == 'X') {
897 radix = 16;
898 ++i;
899 if (i >= count || !str[i]) goto error;
900 }
901 else
902 radix = 8;
903 }
904 else
905 radix = 10;
906 }
907
908 // We have the radix.
909 max_ui_pre1 = max_ui / (T_bin)radix;
910 max_ui_pre2 = max_ui % (T_bin)radix;
911 for (;;) {
912 if ('0' <= str[i] && str[i] <= '9')
913 digit = (T_bin)str[i] - '0';
914 else if ('A' <= str[i] && str[i] <= 'Z')
915 digit = (T_bin)str[i] - 'A' + '\x0a';
916 else if ('a' <= str[i] && str[i] <= 'z')
917 digit = (T_bin)str[i] - 'a' + '\x0a';
918 else
919 goto error;
920 if (digit >= (T_bin)radix)
921 goto error;
922
923 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
924 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
925 value = value * (T_bin)radix + digit;
926 else {
927 // Overflow!
928 flags |= 0x02;
929 }
930
931 ++i;
932 if (i >= count || !str[i])
933 goto error;
934 }
935
936 error:
937 if (end) *end = i;
938 return value;
939 }
941
952 template <class T, class T_bin>
953 T_bin strtoint(
954 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
955 _Out_opt_ size_t* end,
956 _In_ int radix)
957 {
958 uint8_t flags;
959 T_bin value;
960
961 switch (sizeof(T_bin)) {
962 case 1:
963 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
964 if ((flags & 0x01) && (value & 0x80)) {
965 // Sign bit is 1 => overflow.
966 flags |= 0x02;
967 }
968 return (flags & 0x02) ?
969 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
970 (flags & 0x01) ? -value : value;
971
972 case 2:
973 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
974 if ((flags & 0x01) && (value & 0x8000)) {
975 // Sign bit is 1 => overflow.
976 flags |= 0x02;
977 }
978 return (flags & 0x02) ?
979 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
980 (flags & 0x01) ? -value : value;
981
982 case 4:
983 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
984 if ((flags & 0x01) && (value & 0x80000000)) {
985 // Sign bit is 1 => overflow.
986 flags |= 0x02;
987 }
988 return (flags & 0x02) ?
989 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
990 (flags & 0x01) ? -value : value;
991
992 case 8:
993 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
994 if ((flags & 0x01) && (value & 0x8000000000000000)) {
995 // Sign bit is 1 => overflow.
996 flags |= 0x02;
997 }
998 return (flags & 0x02) ?
999 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
1000 (flags & 0x01) ? -value : value;
1001
1002 default:
1003 throw std::invalid_argument("Unsupported bit length");
1004 }
1005 }
1006
1017 template <class T, class T_bin>
1018 inline T_bin strtouint(
1019 _In_reads_or_z_opt_(count) const T* str,
1020 _In_ size_t count,
1021 _Out_opt_ size_t* end,
1022 _In_ int radix)
1023 {
1024 uint8_t flags;
1025 T_bin value;
1026
1027 switch (sizeof(T_bin)) {
1028 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
1029 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
1030 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
1031 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
1032 default: throw std::invalid_argument("Unsupported bit length");
1033 }
1034
1035 return (flags & 0x02) ?
1036 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
1037 (flags & 0x01) ? ~value : value;
1038 }
1039
1050 template <class T>
1051 inline int32_t strto32(
1052 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1053 _Out_opt_ size_t* end,
1054 _In_ int radix)
1055 {
1056 return strtoint<T, int32_t>(str, count, end, radix);
1057 }
1058
1069 template <class T>
1070 inline int64_t strto64(
1071 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1072 _Out_opt_ size_t* end,
1073 _In_ int radix)
1074 {
1075 return strtoint<T, int64_t>(str, count, end, radix);
1076 }
1077
1089 template <class T>
1090 inline intptr_t strtoi(
1091 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1092 _Out_opt_ size_t* end,
1093 _In_ int radix)
1094 {
1095#if defined(_WIN64) || defined(__LP64__)
1096 return (intptr_t)strto64(str, count, end, radix);
1097#else
1098 return (intptr_t)strto32(str, count, end, radix);
1099#endif
1100 }
1101
1112 template <class T>
1113 inline uint32_t strtou32(
1114 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1115 _Out_opt_ size_t* end,
1116 _In_ int radix)
1117 {
1118 return strtouint<T, uint32_t>(str, count, end, radix);
1119 }
1120
1131 template <class T>
1132 inline uint64_t strtou64(
1133 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1134 _Out_opt_ size_t* end,
1135 _In_ int radix)
1136 {
1137 return strtouint<T, uint64_t>(str, count, end, radix);
1138 }
1139
1151 template <class T>
1152 inline size_t strtoui(
1153 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1154 _Out_opt_ size_t* end,
1155 _In_ int radix)
1156 {
1157#if defined(_WIN64) || defined(__LP64__)
1158 return (size_t)strtou64(str, count, end, radix);
1159#else
1160 return (size_t)strtou32(str, count, end, radix);
1161#endif
1162 }
1163
1165 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)
1166 {
1167 int r;
1168#ifdef _WIN32
1169 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1170#pragma warning(suppress: 4996)
1171 r = _vsnprintf_l(str, capacity, format, locale, arg);
1172#else
1173 r = ::vsnprintf(str, capacity, format, arg);
1174#endif
1175 if (r == -1 && strnlen(str, capacity) == capacity) {
1176 // Buffer overrun. Estimate buffer size for the next iteration.
1177 capacity += std::max<size_t>(capacity / 8, 0x80);
1178 if (capacity > INT_MAX)
1179 throw std::invalid_argument("string too big");
1180 return (int)capacity;
1181 }
1182 return r;
1183 }
1184
1185 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)
1186 {
1187 int r;
1188#ifdef _WIN32
1189 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1190#pragma warning(suppress: 4996)
1191 r = _vsnwprintf_l(str, capacity, format, locale, arg);
1192#else
1193 r = vswprintf(str, capacity, format, arg);
1194#endif
1195 if (r == -1 && strnlen(str, capacity) == capacity) {
1196 // Buffer overrun. Estimate buffer size for the next iteration.
1197 capacity += std::max<size_t>(capacity / 8, 0x80);
1198 if (capacity > INT_MAX)
1199 throw std::invalid_argument("string too big");
1200 return (int)capacity;
1201 }
1202 return r;
1203 }
1205
1214 template<class _Elem, class _Traits, class _Ax>
1215 inline void vappendf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, _In_ va_list arg)
1216 {
1217 _Elem buf[1024/sizeof(_Elem)];
1218
1219 // Try with stack buffer first.
1220 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
1221 if (count >= 0) {
1222 // Copy from stack.
1223 str.append(buf, count);
1224 } else {
1225 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1226 // Allocate on heap and retry.
1227 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1228 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
1229 if (count >= 0) {
1230 str.append(buf_dyn.get(), count);
1231 break;
1232 }
1233 }
1234 }
1235 }
1236
1244 template<class _Elem, class _Traits, class _Ax>
1245 inline void appendf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1246 {
1247 va_list arg;
1248 va_start(arg, locale);
1249 vappendf(str, format, locale, arg);
1250 va_end(arg);
1251 }
1252
1261 template<class _Elem, class _Traits, class _Ax>
1262 inline void vsprintf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, _In_ va_list arg)
1263 {
1264 str.clear();
1265 vappendf(str, format, locale, arg);
1266 }
1267
1275 template<class _Elem, class _Traits, class _Ax>
1276 inline void sprintf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1277 {
1278 va_list arg;
1279 va_start(arg, locale);
1280 vsprintf(str, format, locale, arg);
1281 va_end(arg);
1282 }
1283
1293 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1294 inline std::basic_string<_Elem, _Traits, _Ax> vsprintf(_In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, _In_ va_list arg)
1295 {
1296 std::basic_string<_Elem, _Traits, _Ax> str;
1297 vappendf(str, format, locale, arg);
1298 return str;
1299 }
1300
1309 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1310 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1311 {
1312 va_list arg;
1313 va_start(arg, locale);
1314 auto str = vsprintf(format, locale, arg);
1315 va_end(arg);
1316 return str;
1317 }
1318
1320 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)
1321 {
1322#ifdef _WIN32
1323 return _strftime_l(str, capacity, format, time, locale);
1324#else
1325 return strftime_l(str, capacity, format, time, locale);
1326#endif
1327 }
1328
1329 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)
1330 {
1331#ifdef _WIN32
1332 return _wcsftime_l(str, capacity, format, time, locale);
1333#else
1334 return wcsftime_l(str, capacity, format, time, locale);
1335#endif
1336 }
1338
1347 template<class _Elem, class _Traits, class _Ax>
1348 inline void strcatftime(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_ const _Elem *format, _In_ const struct tm* time, _In_opt_ locale_t locale)
1349 {
1350 _Elem buf[1024/sizeof(_Elem)];
1351
1352 // Try with stack buffer first.
1353 size_t count = strftime(buf, _countof(buf), format, time, locale);
1354 if (count) {
1355 // Copy from stack.
1356 str.append(buf, count);
1357 } else {
1358 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1359 // Allocate on heap and retry.
1360 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1361 count = strftime(buf_dyn.get(), capacity, format, time, locale);
1362 if (count) {
1363 str.append(buf_dyn.get(), count);
1364 break;
1365 }
1366 }
1367 }
1368 }
1369
1378 template<class _Elem, class _Traits, class _Ax>
1379 inline void strftime(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_ const _Elem *format, _In_ const struct tm* time, _In_opt_ locale_t locale)
1380 {
1381 str.clear();
1382 strcatftime(str, format, time, locale);
1383 }
1384
1395 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1396 inline std::basic_string<_Elem, _Traits, _Ax> strftime(_In_z_ _Printf_format_string_ const _Elem *format, _In_ const struct tm* time, _In_opt_ locale_t locale)
1397 {
1398 std::basic_string<_Elem, _Traits, _Ax> str;
1399 strcatftime(str, format, time, locale);
1400 return str;
1401 }
1402
1410 template<class T>
1411 inline void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
1412 {
1413 _Assume_(str);
1414 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1415 for (size_t i = 0; str[i]; ++i)
1416 str[i] = ctype.tolower(str[i]);
1417 }
1418
1427 template<class T>
1428 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1429 {
1430 _Assume_(str || !count);
1431 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1432 for (size_t i = 0; i < count && str[i]; ++i)
1433 str[i] = ctype.tolower(str[i]);
1434 }
1435
1443 template<class T>
1444 inline void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
1445 {
1446 _Assume_(str);
1447 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1448 for (size_t i = 0; str[i]; ++i)
1449 str[i] = ctype.toupper(str[i]);
1450 }
1451
1460 template<class T>
1461 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1462 {
1463 _Assume_(str || !count);
1464 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1465 for (size_t i = 0; i < count && str[i]; ++i)
1466 str[i] = ctype.toupper(str[i]);
1467 }
1468
1476 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1477 inline void strupr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str, _In_ const std::locale& locale)
1478 {
1479 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1480 for (size_t i = 0; i < str.size(); ++i)
1481 str[i] = ctype.toupper(str[i]);
1482 }
1483
1492 template<class T>
1493 inline size_t ltrim(
1494 _Inout_z_count_(count) T* str, _In_ size_t count,
1495 _In_ const std::locale& locale)
1496 {
1497 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1498 for (size_t i = 0;; ++i) {
1499 if (i >= count) {
1500 if (count) str[0] = 0;
1501 return 0;
1502 }
1503 if (!str[i]) {
1504 str[0] = 0;
1505 return 0;
1506 }
1507 if (!ctype.is(ctype.space, str[i])) {
1508 if (!i)
1509 return strnlen(str, count);
1510 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
1511 str[n] = 0;
1512 return n;
1513 }
1514 }
1515 }
1516
1522 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1523 inline void ltrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1524 {
1525 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1526 s.erase(
1527 s.begin(),
1528 std::find_if(
1529 s.begin(),
1530 s.end(),
1531 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }));
1532 }
1533
1542 template<class T>
1543 inline size_t rtrim(
1544 _Inout_z_count_(count) T* str, _In_ size_t count,
1545 _In_ const std::locale& locale)
1546 {
1547 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1548 for (size_t i = 0, j = 0;;) {
1549 if (i >= count || !str[i]) {
1550 if (j < count) str[j] = 0;
1551 return j;
1552 }
1553 if (!ctype.is(ctype.space, str[i]))
1554 j = ++i;
1555 else
1556 ++i;
1557 }
1558 }
1559
1565 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1566 static inline void rtrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1567 {
1568 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1569 s.erase(
1570 std::find_if(
1571 s.rbegin(),
1572 s.rend(),
1573 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
1574 s.end());
1575 }
1576
1585 template<class T>
1586 inline size_t trim(
1587 _Inout_z_count_(count) T* str, _In_ size_t count,
1588 _In_ const std::locale& locale)
1589 {
1590 return ltrim(str, rtrim(str, count, locale), locale);
1591 }
1592
1598 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1599 static inline void trim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1600 {
1601 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1602 s.erase(
1603 s.begin(),
1604 std::find_if(
1605 s.begin(),
1606 s.end(),
1607 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }));
1608 s.erase(
1609 std::find_if(
1610 s.rbegin(),
1611 s.rend(),
1612 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
1613 s.end());
1614 }
1615}