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
253 const inline std::locale std_locale_C("C");
254
263 template <class T>
264 inline bool isblank(
265 _In_reads_or_z_opt_(count) const T* str,
266 _In_ size_t count,
267 _In_ const std::locale& locale)
268 {
269 _Assume_(str || !count);
270 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
271 for (size_t i = 0; i < count && str[i]; ++i)
272 if (!ctype.is(ctype.space, str[i]))
273 return false;
274 return true;
275 }
276
286 template <class T>
287 inline size_t strnichr(
288 _In_reads_or_z_opt_(count) const T* str,
289 _In_ size_t count,
290 _In_ T chr,
291 _In_ const std::locale& locale)
292 {
293 _Assume_(str || !count);
294 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
295 chr = ctype.tolower(chr);
296 for (size_t i = 0; i < count && str[i]; ++i)
297 if (ctype.tolower(str[i]) == chr) return i;
298 return npos;
299 }
300
310 template <class T>
311 inline size_t strrnichr(
312 _In_reads_or_z_opt_(count) const T* str,
313 _In_ size_t count,
314 _In_ T chr,
315 _In_ const std::locale& locale)
316 {
317 _Assume_(str || !count);
318 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
319 chr = ctype.tolower(chr);
320 size_t z = npos;
321 for (size_t i = 0; i < count && str[i]; ++i)
322 if (ctype.tolower(str[i]) == chr) z = i;
323 return z;
324 }
325
334 template <class T1, class T2>
335 inline int strcmp(const T1* str1, const T2* str2)
336 {
337 _Assume_(str1 && str2);
338 T1 a; T2 b;
339 for (size_t i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
340 if (a > b) return +1;
341 if (a < b) return -1;
342 }
343 return 0;
344 }
345
356 template <class T1, class T2>
357 inline int strncmp(
358 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
359 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
360 {
361 _Assume_(str1 || !count1);
362 _Assume_(str2 || !count2);
363 size_t i; T1 a; T2 b;
364 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
365 if (a > b) return +1;
366 if (a < b) return -1;
367 }
368 if (i < count1 && str1[i]) return +1;
369 if (i < count2 && str2[i]) return -1;
370 return 0;
371 }
372
382 template <class T1, class T2>
383 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)
384 {
385 _Assume_((str1 && str2) || !count);
386 size_t i; T1 a; T2 b;
387 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
388 if (a > b) return +1;
389 if (a < b) return -1;
390 }
391 if (i < count && str1[i]) return +1;
392 if (i < count && str2[i]) return -1;
393 return 0;
394 }
395
406 template <class T>
407 inline int strncoll(
408 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
409 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
410 _In_ const std::locale& locale)
411 {
412 _Assume_(str1 || !count1);
413 _Assume_(str2 || !count2);
414 auto& collate = std::use_facet<std::collate<T>>(locale);
415 return collate.compare(str1, str1 + count1, str2, str2 + count2);
416 }
417
426 template <class T1, class T2>
427 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
428 {
429 _Assume_(str1);
430 _Assume_(str2);
431 size_t i; T1 a; T2 b;
432 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
433 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
434 for (i = 0; (a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i])); i++) {
435 if (a > b) return +1;
436 if (a < b) return -1;
437 }
438 if (str1[i]) return +1;
439 if (str2[i]) return -1;
440 return 0;
441 }
442
452 template <class T1, class T2>
453 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)
454 {
455 _Assume_(str1 || !count);
456 _Assume_(str2 || !count);
457 size_t i; T1 a; T2 b;
458 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
459 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
460 for (i = 0; i < count && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
461 if (a > b) return +1;
462 if (a < b) return -1;
463 }
464 if (i < count && str1[i]) return +1;
465 if (i < count && str2[i]) return -1;
466 return 0;
467 }
468
479 template <class T1, class T2>
480 inline int strnicmp(
481 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
482 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
483 _In_ const std::locale& locale)
484 {
485 _Assume_(str1 || !count1);
486 _Assume_(str2 || !count2);
487 size_t i; T1 a; T2 b;
488 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
489 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
490 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
491 if (a > b) return +1;
492 if (a < b) return -1;
493 }
494 if (i < count1 && str1[i]) return +1;
495 if (i < count2 && str2[i]) return -1;
496 return 0;
497 }
498
507 template <class T1, class T2>
508 inline size_t strstr(
509 _In_z_ const T1* str,
510 _In_z_ const T2* sample)
511 {
512 _Assume_(str);
513 _Assume_(sample);
514 for (size_t offset = 0;; ++offset) {
515 for (size_t i = offset, j = 0;; ++i, ++j) {
516 if (!sample[j])
517 return offset;
518 if (!str[i])
519 return npos;
520 if (str[i] != sample[j])
521 break;
522 }
523 }
524 }
525
535 template <class T1, class T2>
536 inline size_t strnstr(
537 _In_reads_or_z_opt_(count) const T1* str,
538 _In_ size_t count,
539 _In_z_ const T2* sample)
540 {
541 _Assume_(str || !count);
542 _Assume_(sample);
543 for (size_t offset = 0;; ++offset) {
544 for (size_t i = offset, j = 0;; ++i, ++j) {
545 if (!sample[j])
546 return offset;
547 if (i >= count || !str[i])
548 return npos;
549 if (str[i] != sample[j])
550 break;
551 }
552 }
553 }
554
563 template <class T1, class T2>
564 inline size_t stristr(
565 _In_z_ const T1* str,
566 _In_z_ const T2* sample,
567 _In_ const std::locale& locale)
568 {
569 _Assume_(str);
570 _Assume_(sample);
571 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
572 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
573 for (size_t offset = 0;; ++offset) {
574 for (size_t i = offset, j = 0;; ++i, ++j) {
575 if (!sample[j])
576 return offset;
577 if (!str[i])
578 return npos;
579 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
580 break;
581 }
582 }
583 }
584
594 template <class T1, class T2>
595 inline size_t strnistr(
596 _In_reads_or_z_opt_(count) const T1* str,
597 _In_ size_t count,
598 _In_z_ const T2* sample,
599 _In_ const std::locale& locale)
600 {
601 _Assume_(str || !count);
602 _Assume_(sample);
603 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
604 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
605 for (size_t offset = 0;; ++offset) {
606 for (size_t i = offset, j = 0;; ++i, ++j) {
607 if (!sample[j])
608 return offset;
609 if (i >= count || !str[i])
610 return npos;
611 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
612 break;
613 }
614 }
615 }
616
625 template <class T1, class T2>
626 inline size_t strcpy(
627 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
628 _In_z_ const T2* src)
629 {
630 _Assume_(dst && src);
631 for (size_t i = 0; ; ++i) {
632 if ((dst[i] = src[i]) == 0)
633 return i;
634 }
635 }
636
646 template <class T1, class T2>
647 inline size_t strncpy(
648 _Out_writes_(count) _Post_maybez_ T1* dst,
649 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
650 {
651 _Assume_(dst && src || !count);
652 for (size_t i = 0; ; ++i) {
653 if (i >= count)
654 return i;
655 if ((dst[i] = src[i]) == 0)
656 return i;
657 }
658 }
659
670 template <class T1, class T2>
671 inline size_t strncpy(
672 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
673 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
674 {
675 _Assume_(dst || !count_dst);
676 _Assume_(src || !count_src);
677 for (size_t i = 0; ; ++i)
678 {
679 if (i >= count_dst)
680 return i;
681 if (i >= count_src) {
682 dst[i] = 0;
683 return i;
684 }
685 if ((dst[i] = src[i]) == 0)
686 return i;
687 }
688 }
689
698 template <class T1, class T2>
699 inline size_t strcat(
700 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
701 _In_z_ const T2* src)
702 {
703 _Assume_(dst && src);
704 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
705 if ((dst[j] = src[i]) == 0)
706 return j;
707 }
708 }
709
719 template <class T1, class T2>
720 inline size_t strncat(
721 _Inout_z_ T1* dst,
722 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
723 {
724 _Assume_(dst && src || !count);
725 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
726 if (i >= count)
727 return j;
728 if ((dst[j] = src[i]) == 0)
729 return j;
730 }
731 }
732
743 template <class T1, class T2>
744 inline size_t strncat(
745 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
746 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
747 {
748 _Assume_(dst || !count_dst);
749 _Assume_(src || !count_src);
750 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
751 {
752 if (j >= count_dst)
753 return j;
754 if (i >= count_src) {
755 dst[j] = 0;
756 return j;
757 }
758 if ((dst[j] = src[i]) == 0)
759 return j;
760 }
761 }
762
773 template <class T>
774 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
775 {
776 if (!str) _Unlikely_
777 return nullptr;
778 size_t count = strlen(str) + 1;
779 T* dst = new T[count];
780 strncpy(dst, count, str, SIZE_MAX);
781 return dst;
782 }
783
795 template <class T>
796 inline _Ret_z_ T* strndup(
797 _In_reads_or_z_opt_(count) const T* str,
798 _In_ size_t count)
799 {
800 T* dst = new T[count];
801 strncpy(dst, count, str, SIZE_MAX);
802 return dst;
803 }
804
814 template <class T>
815 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
816 {
817 _Assume_(dst);
818 _Assume_(src);
819 size_t i, j;
820 for (i = j = 0; src[j];) {
821 if (src[j] != '\r' || src[j + 1] != '\n')
822 dst[i++] = src[j++];
823 else {
824 dst[i++] = '\n';
825 j += 2;
826 }
827 }
828 dst[i] = 0;
829 return i;
830 }
831
838 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
839 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &dst, _In_z_ const _Elem* src)
840 {
841 _Assume_(src);
842 _Assume_(src != dst.c_str());
843 dst.clear();
844 dst.reserve(strlen(src));
845 for (size_t j = 0; src[j];) {
846 if (src[j] != '\r' || src[j + 1] != '\n')
847 dst += src[j++];
848 else {
849 dst += '\n';
850 j += 2;
851 }
852 }
853 }
854
860 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
861 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
862 {
863 size_t i, j, n;
864 for (i = j = 0, n = str.size(); j < n;) {
865 if (str[j] != '\r' || str[j + 1] != '\n')
866 str[i++] = str[j++];
867 else {
868 str[i++] = '\n';
869 j += 2;
870 }
871 }
872 str.resize(i);
873 }
874
876 template <class T, class T_bin>
877 inline T_bin strtoint(
878 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
879 _Out_opt_ size_t* end,
880 _In_ int radix,
881 _Out_ uint8_t& flags)
882 {
883 _Assume_(str || !count);
884 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
885
886 size_t i = 0;
887 T_bin value = 0, digit,
888 max_ui = (T_bin)-1,
889 max_ui_pre1, max_ui_pre2;
890
891 flags = 0;
892
893 // Skip leading spaces.
894 for (;; ++i) {
895 if (i >= count || !str[i]) goto error;
896 if (!isspace(str[i])) break;
897 }
898
899 // Read the sign.
900 if (str[i] == '+') {
901 flags &= ~0x01;
902 ++i;
903 if (i >= count || !str[i]) goto error;
904 }
905 else if (str[i] == '-') {
906 flags |= 0x01;
907 ++i;
908 if (i >= count || !str[i]) goto error;
909 }
910
911 if (radix == 16) {
912 // On hexadecimal, allow leading 0x.
913 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
914 i += 2;
915 if (i >= count || !str[i]) goto error;
916 }
917 }
918 else if (!radix) {
919 // Autodetect radix.
920 if (str[i] == '0') {
921 ++i;
922 if (i >= count || !str[i]) goto error;
923 if (str[i] == 'x' || str[i] == 'X') {
924 radix = 16;
925 ++i;
926 if (i >= count || !str[i]) goto error;
927 }
928 else
929 radix = 8;
930 }
931 else
932 radix = 10;
933 }
934
935 // We have the radix.
936 max_ui_pre1 = max_ui / (T_bin)radix;
937 max_ui_pre2 = max_ui % (T_bin)radix;
938 for (;;) {
939 if ('0' <= str[i] && str[i] <= '9')
940 digit = (T_bin)str[i] - '0';
941 else if ('A' <= str[i] && str[i] <= 'Z')
942 digit = (T_bin)str[i] - 'A' + '\x0a';
943 else if ('a' <= str[i] && str[i] <= 'z')
944 digit = (T_bin)str[i] - 'a' + '\x0a';
945 else
946 goto error;
947 if (digit >= (T_bin)radix)
948 goto error;
949
950 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
951 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
952 value = value * (T_bin)radix + digit;
953 else {
954 // Overflow!
955 flags |= 0x02;
956 }
957
958 ++i;
959 if (i >= count || !str[i])
960 goto error;
961 }
962
963 error:
964 if (end) *end = i;
965 return value;
966 }
968
979 template <class T, class T_bin>
980 T_bin strtoint(
981 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
982 _Out_opt_ size_t* end,
983 _In_ int radix)
984 {
985 uint8_t flags;
986 T_bin value;
987
988 switch (sizeof(T_bin)) {
989 case 1:
990 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
991 if ((flags & 0x01) && (value & 0x80)) {
992 // Sign bit is 1 => overflow.
993 flags |= 0x02;
994 }
995 return (flags & 0x02) ?
996 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
997 (flags & 0x01) ? -value : value;
998
999 case 2:
1000 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
1001 if ((flags & 0x01) && (value & 0x8000)) {
1002 // Sign bit is 1 => overflow.
1003 flags |= 0x02;
1004 }
1005 return (flags & 0x02) ?
1006 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
1007 (flags & 0x01) ? -value : value;
1008
1009 case 4:
1010 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
1011 if ((flags & 0x01) && (value & 0x80000000)) {
1012 // Sign bit is 1 => overflow.
1013 flags |= 0x02;
1014 }
1015 return (flags & 0x02) ?
1016 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
1017 (flags & 0x01) ? -value : value;
1018
1019 case 8:
1020 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
1021 if ((flags & 0x01) && (value & 0x8000000000000000)) {
1022 // Sign bit is 1 => overflow.
1023 flags |= 0x02;
1024 }
1025 return (flags & 0x02) ?
1026 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
1027 (flags & 0x01) ? -value : value;
1028
1029 default:
1030 throw std::invalid_argument("Unsupported bit length");
1031 }
1032 }
1033
1044 template <class T, class T_bin>
1045 inline T_bin strtouint(
1046 _In_reads_or_z_opt_(count) const T* str,
1047 _In_ size_t count,
1048 _Out_opt_ size_t* end,
1049 _In_ int radix)
1050 {
1051 uint8_t flags;
1052 T_bin value;
1053
1054 switch (sizeof(T_bin)) {
1055 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
1056 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
1057 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
1058 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
1059 default: throw std::invalid_argument("Unsupported bit length");
1060 }
1061
1062 return (flags & 0x02) ?
1063 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
1064 (flags & 0x01) ? ~value : value;
1065 }
1066
1077 template <class T>
1078 inline int32_t strto32(
1079 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1080 _Out_opt_ size_t* end,
1081 _In_ int radix)
1082 {
1083 return strtoint<T, int32_t>(str, count, end, radix);
1084 }
1085
1096 template <class T>
1097 inline int64_t strto64(
1098 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1099 _Out_opt_ size_t* end,
1100 _In_ int radix)
1101 {
1102 return strtoint<T, int64_t>(str, count, end, radix);
1103 }
1104
1116 template <class T>
1117 inline intptr_t strtoi(
1118 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1119 _Out_opt_ size_t* end,
1120 _In_ int radix)
1121 {
1122#if defined(_WIN64) || defined(__LP64__)
1123 return (intptr_t)strto64(str, count, end, radix);
1124#else
1125 return (intptr_t)strto32(str, count, end, radix);
1126#endif
1127 }
1128
1139 template <class T>
1140 inline uint32_t strtou32(
1141 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1142 _Out_opt_ size_t* end,
1143 _In_ int radix)
1144 {
1145 return strtouint<T, uint32_t>(str, count, end, radix);
1146 }
1147
1158 template <class T>
1159 inline uint64_t strtou64(
1160 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1161 _Out_opt_ size_t* end,
1162 _In_ int radix)
1163 {
1164 return strtouint<T, uint64_t>(str, count, end, radix);
1165 }
1166
1178 template <class T>
1179 inline size_t strtoui(
1180 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1181 _Out_opt_ size_t* end,
1182 _In_ int radix)
1183 {
1184#if defined(_WIN64) || defined(__LP64__)
1185 return (size_t)strtou64(str, count, end, radix);
1186#else
1187 return (size_t)strtou32(str, count, end, radix);
1188#endif
1189 }
1190
1192 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)
1193 {
1194 int r;
1195#ifdef _WIN32
1196 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1197#pragma warning(suppress: 4996)
1198 r = _vsnprintf_l(str, capacity, format, locale, arg);
1199#else
1200 r = ::vsnprintf(str, capacity, format, arg);
1201#endif
1202 if (r == -1 && strnlen(str, capacity) == capacity) {
1203 // Buffer overrun. Estimate buffer size for the next iteration.
1204 capacity += std::max<size_t>(capacity / 8, 0x80);
1205 if (capacity > INT_MAX)
1206 throw std::invalid_argument("string too big");
1207 return (int)capacity;
1208 }
1209 return r;
1210 }
1211
1212 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)
1213 {
1214 int r;
1215#ifdef _WIN32
1216 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1217#pragma warning(suppress: 4996)
1218 r = _vsnwprintf_l(str, capacity, format, locale, arg);
1219#else
1220 r = vswprintf(str, capacity, format, arg);
1221#endif
1222 if (r == -1 && strnlen(str, capacity) == capacity) {
1223 // Buffer overrun. Estimate buffer size for the next iteration.
1224 capacity += std::max<size_t>(capacity / 8, 0x80);
1225 if (capacity > INT_MAX)
1226 throw std::invalid_argument("string too big");
1227 return (int)capacity;
1228 }
1229 return r;
1230 }
1232
1243 template<class _Elem, class _Traits, class _Ax>
1244 inline size_t 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)
1245 {
1246 _Elem buf[1024/sizeof(_Elem)];
1247
1248 // Try with stack buffer first.
1249 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
1250 if (count >= 0) {
1251 // Copy from stack.
1252 str.append(buf, count);
1253 return count;
1254 }
1255 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1256 // Allocate on heap and retry.
1257 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1258 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
1259 if (count >= 0) {
1260 str.append(buf_dyn.get(), count);
1261 return count;
1262 }
1263 }
1264 }
1265
1275 template<class _Elem, class _Traits, class _Ax>
1276 inline size_t appendf(_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 size_t n = vappendf(str, format, locale, arg);
1281 va_end(arg);
1282 return n;
1283 }
1284
1293 template<class _Elem, class _Traits, class _Ax>
1294 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)
1295 {
1296 str.clear();
1297 vappendf(str, format, locale, arg);
1298 }
1299
1307 template<class _Elem, class _Traits, class _Ax>
1308 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, ...)
1309 {
1310 va_list arg;
1311 va_start(arg, locale);
1312 vsprintf(str, format, locale, arg);
1313 va_end(arg);
1314 }
1315
1325 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1326 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)
1327 {
1328 std::basic_string<_Elem, _Traits, _Ax> str;
1329 vappendf(str, format, locale, arg);
1330 return str;
1331 }
1332
1341 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1342 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1343 {
1344 va_list arg;
1345 va_start(arg, locale);
1346 auto str = vsprintf(format, locale, arg);
1347 va_end(arg);
1348 return str;
1349 }
1350
1352 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)
1353 {
1354#ifdef _WIN32
1355 return _strftime_l(str, capacity, format, time, locale);
1356#else
1357 return strftime_l(str, capacity, format, time, locale);
1358#endif
1359 }
1360
1361 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)
1362 {
1363#ifdef _WIN32
1364 return _wcsftime_l(str, capacity, format, time, locale);
1365#else
1366 return wcsftime_l(str, capacity, format, time, locale);
1367#endif
1368 }
1370
1379 template<class _Elem, class _Traits, class _Ax>
1380 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)
1381 {
1382 _Elem buf[1024/sizeof(_Elem)];
1383
1384 // Try with stack buffer first.
1385 size_t count = strftime(buf, _countof(buf), format, time, locale);
1386 if (count) {
1387 // Copy from stack.
1388 str.append(buf, count);
1389 } else {
1390 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1391 // Allocate on heap and retry.
1392 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1393 count = strftime(buf_dyn.get(), capacity, format, time, locale);
1394 if (count) {
1395 str.append(buf_dyn.get(), count);
1396 break;
1397 }
1398 }
1399 }
1400 }
1401
1410 template<class _Elem, class _Traits, class _Ax>
1411 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)
1412 {
1413 str.clear();
1414 strcatftime(str, format, time, locale);
1415 }
1416
1427 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1428 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)
1429 {
1430 std::basic_string<_Elem, _Traits, _Ax> str;
1431 strcatftime(str, format, time, locale);
1432 return str;
1433 }
1434
1442 template<class T>
1443 inline void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
1444 {
1445 _Assume_(str);
1446 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1447 for (size_t i = 0; str[i]; ++i)
1448 str[i] = ctype.tolower(str[i]);
1449 }
1450
1459 template<class T>
1460 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1461 {
1462 _Assume_(str || !count);
1463 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1464 for (size_t i = 0; i < count && str[i]; ++i)
1465 str[i] = ctype.tolower(str[i]);
1466 }
1467
1475 template<class T>
1476 inline void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
1477 {
1478 _Assume_(str);
1479 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1480 for (size_t i = 0; str[i]; ++i)
1481 str[i] = ctype.toupper(str[i]);
1482 }
1483
1492 template<class T>
1493 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1494 {
1495 _Assume_(str || !count);
1496 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1497 for (size_t i = 0; i < count && str[i]; ++i)
1498 str[i] = ctype.toupper(str[i]);
1499 }
1500
1508 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1509 inline void strupr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str, _In_ const std::locale& locale)
1510 {
1511 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1512 for (size_t i = 0; i < str.size(); ++i)
1513 str[i] = ctype.toupper(str[i]);
1514 }
1515
1524 template<class T>
1525 inline size_t ltrim(
1526 _Inout_z_count_(count) T* str, _In_ size_t count,
1527 _In_ const std::locale& locale)
1528 {
1529 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1530 for (size_t i = 0;; ++i) {
1531 if (i >= count) {
1532 if (count) str[0] = 0;
1533 return 0;
1534 }
1535 if (!str[i]) {
1536 str[0] = 0;
1537 return 0;
1538 }
1539 if (!ctype.is(ctype.space, str[i])) {
1540 if (!i)
1541 return strnlen(str, count);
1542 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
1543 str[n] = 0;
1544 return n;
1545 }
1546 }
1547 }
1548
1554 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1555 inline void ltrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1556 {
1557 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1558 s.erase(
1559 s.begin(),
1560 std::find_if(
1561 s.begin(),
1562 s.end(),
1563 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }));
1564 }
1565
1574 template<class T>
1575 inline size_t rtrim(
1576 _Inout_z_count_(count) T* str, _In_ size_t count,
1577 _In_ const std::locale& locale)
1578 {
1579 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1580 for (size_t i = 0, j = 0;;) {
1581 if (i >= count || !str[i]) {
1582 if (j < count) str[j] = 0;
1583 return j;
1584 }
1585 if (!ctype.is(ctype.space, str[i]))
1586 j = ++i;
1587 else
1588 ++i;
1589 }
1590 }
1591
1597 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1598 static inline void rtrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1599 {
1600 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1601 s.erase(
1602 std::find_if(
1603 s.rbegin(),
1604 s.rend(),
1605 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
1606 s.end());
1607 }
1608
1617 template<class T>
1618 inline size_t trim(
1619 _Inout_z_count_(count) T* str, _In_ size_t count,
1620 _In_ const std::locale& locale)
1621 {
1622 return ltrim(str, rtrim(str, count, locale), locale);
1623 }
1624
1630 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1631 static inline void trim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1632 {
1633 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1634 s.erase(
1635 s.begin(),
1636 std::find_if(
1637 s.begin(),
1638 s.end(),
1639 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }));
1640 s.erase(
1641 std::find_if(
1642 s.rbegin(),
1643 s.rend(),
1644 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
1645 s.end());
1646 }
1647}