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 bool islbreak(_In_ T chr)
113 {
114 return chr == '\n' || chr == '\r';
115 }
116
125 template <class T>
126 inline size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
127 {
128 _Assume_(chr || !count);
129 if (count >= 2 && ((chr[0] == '\r' && chr[1] == '\n') || (chr[0] == '\n' && chr[1] == '\r')))
130 return 2;
131 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
132 return 1;
133 return 0;
134 }
135
141 template <class T>
142 inline bool isspace(_In_ T chr)
143 {
144 return chr == ' ' || chr == '\t' || chr == '\n' || chr == '\r' || chr == '\v' || chr == '\f';
145 }
146
152 template <class T>
153 inline bool islower(_In_ T chr)
154 {
155 return 'a' <= chr && chr <= 'z';
156 }
157
163 template <class T>
164 inline bool isupper(_In_ T chr)
165 {
166 return 'A' <= chr && chr <= 'Z';
167 }
168
174 template <class T>
175 inline bool isdigit(_In_ T chr)
176 {
177 return '0' <= chr && chr <= '9';
178 }
179
185 template <class T>
186 inline bool isalpha(_In_ T chr)
187 {
188 return islower(chr) || isupper(chr);
189 }
190
197 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
198 {
199 _Assume_(glyph || !count);
200 if (count) {
201#ifdef _WIN32
202 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
203#else
204 size_t i = 1;
205#endif
206 for (; i < count && iscombining(glyph[i]); ++i);
207 return i;
208 }
209 return 0;
210 }
211
219 template <class T>
220 inline T tolower(_In_ T chr)
221 {
222 return isupper(chr) ? chr | 0x20 : chr;
223 }
224
232 template <class T>
233 inline T toupper(_In_ T chr)
234 {
235 return islower(chr) ? chr | ~0x20 : chr;
236 }
237
245 template <class T>
246 inline size_t strlen(_In_z_ const T* str)
247 {
248 _Assume_(str);
249 size_t i;
250 for (i = 0; str[i]; ++i);
251 return i;
252 }
253
262 template <class T>
263 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
264 {
265 _Assume_(str || !count);
266 size_t i;
267 for (i = 0; i < count && str[i]; ++i);
268 return i;
269 }
270
271 constexpr auto npos{ static_cast<size_t>(-1) };
272
281 template <class T>
282 inline size_t strchr(_In_z_ const T* str, _In_ T chr)
283 {
284 _Assume_(str);
285 for (size_t i = 0; str[i]; ++i)
286 if (str[i] == chr) return i;
287 return npos;
288 }
289
299 template <class T>
300 inline size_t strnchr(
301 _In_reads_or_z_opt_(count) const T* str,
302 _In_ size_t count,
303 _In_ T chr)
304 {
305 _Assume_(str || !count);
306 for (size_t i = 0; i < count && str[i]; ++i)
307 if (str[i] == chr) return i;
308 return npos;
309 }
310
320 template <class T>
321 inline size_t strrnchr(
322 _In_reads_or_z_opt_(count) const T* str,
323 _In_ size_t count,
324 _In_ T chr)
325 {
326 _Assume_(str || !count);
327 size_t z = npos;
328 for (size_t i = 0; i < count && str[i]; ++i)
329 if (str[i] == chr) z = i;
330 return z;
331 }
332
341 template <class T>
342 inline bool isblank(
343 _In_reads_or_z_opt_(count) const T* str,
344 _In_ size_t count)
345 {
346 _Assume_(str || !count);
347 for (size_t i = 0; i < count && str[i]; ++i)
348 if (!isspace(str[i]))
349 return false;
350 return true;
351 }
352
362 template <class T>
363 inline bool isblank(
364 _In_reads_or_z_opt_(count) const T* str,
365 _In_ size_t count,
366 _In_ const std::locale& locale)
367 {
368 _Assume_(str || !count);
369 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
370 for (size_t i = 0; i < count && str[i]; ++i)
371 if (!ctype.is(ctype.space, str[i]))
372 return false;
373 return true;
374 }
375
385 template <class T>
386 inline size_t strnichr(
387 _In_reads_or_z_opt_(count) const T* str,
388 _In_ size_t count,
389 _In_ T chr)
390 {
391 _Assume_(str || !count);
392 chr = tolower(chr);
393 for (size_t i = 0; i < count && str[i]; ++i)
394 if (tolower(str[i]) == chr) return i;
395 return npos;
396 }
397
408 template <class T>
409 inline size_t strnichr(
410 _In_reads_or_z_opt_(count) const T* str,
411 _In_ size_t count,
412 _In_ T chr,
413 _In_ const std::locale& locale)
414 {
415 _Assume_(str || !count);
416 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
417 chr = ctype.tolower(chr);
418 for (size_t i = 0; i < count && str[i]; ++i)
419 if (ctype.tolower(str[i]) == chr) return i;
420 return npos;
421 }
422
432 template <class T>
433 inline size_t strrnichr(
434 _In_reads_or_z_opt_(count) const T* str,
435 _In_ size_t count,
436 _In_ T chr)
437 {
438 _Assume_(str || !count);
439 chr = tolower(chr);
440 size_t z = npos;
441 for (size_t i = 0; i < count && str[i]; ++i)
442 if (tolower(str[i]) == chr) z = i;
443 return z;
444 }
445
456 template <class T>
457 inline size_t strrnichr(
458 _In_reads_or_z_opt_(count) const T* str,
459 _In_ size_t count,
460 _In_ T chr,
461 _In_ const std::locale& locale)
462 {
463 _Assume_(str || !count);
464 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
465 chr = ctype.tolower(chr);
466 size_t z = npos;
467 for (size_t i = 0; i < count && str[i]; ++i)
468 if (ctype.tolower(str[i]) == chr) z = i;
469 return z;
470 }
471
480 template <class T1, class T2>
481 inline int strcmp(const T1* str1, const T2* str2)
482 {
483 _Assume_(str1 && str2);
484 T1 a; T2 b;
485 for (size_t i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
486 if (a > b) return +1;
487 if (a < b) return -1;
488 }
489 return 0;
490 }
491
502 template <class T1, class T2>
503 inline int strncmp(
504 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
505 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
506 {
507 _Assume_(str1 || !count1);
508 _Assume_(str2 || !count2);
509 size_t i; T1 a; T2 b;
510 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
511 if (a > b) return +1;
512 if (a < b) return -1;
513 }
514 if (i < count1 && str1[i]) return +1;
515 if (i < count2 && str2[i]) return -1;
516 return 0;
517 }
518
528 template <class T1, class T2>
529 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)
530 {
531 _Assume_((str1 && str2) || !count);
532 size_t i; T1 a; T2 b;
533 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
534 if (a > b) return +1;
535 if (a < b) return -1;
536 }
537 if (i < count && str1[i]) return +1;
538 if (i < count && str2[i]) return -1;
539 return 0;
540 }
541
553 template <class T>
554 inline int strncoll(
555 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
556 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
557 _In_ const std::locale& locale)
558 {
559 _Assume_(str1 || !count1);
560 _Assume_(str2 || !count2);
561 auto& collate = std::use_facet<std::collate<T>>(locale);
562 return collate.compare(str1, str1 + count1, str2, str2 + count2);
563 }
564
573 template <class T1, class T2>
574 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
575 {
576 _Assume_(str1);
577 _Assume_(str2);
578 size_t i; T1 a; T2 b;
579 for (i = 0; (a = tolower(str1[i])) | (b = tolower(str2[i])); i++) {
580 if (a > b) return +1;
581 if (a < b) return -1;
582 }
583 if (str1[i]) return +1;
584 if (str2[i]) return -1;
585 return 0;
586 }
587
597 template <class T1, class T2>
598 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
599 {
600 _Assume_(str1);
601 _Assume_(str2);
602 size_t i; T1 a; T2 b;
603 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
604 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
605 for (i = 0; (a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i])); i++) {
606 if (a > b) return +1;
607 if (a < b) return -1;
608 }
609 if (str1[i]) return +1;
610 if (str2[i]) return -1;
611 return 0;
612 }
613
623 template <class T1, class T2>
624 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)
625 {
626 _Assume_(str1 || !count);
627 _Assume_(str2 || !count);
628 size_t i; T1 a; T2 b;
629 for (i = 0; i < count && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); i++) {
630 if (a > b) return +1;
631 if (a < b) return -1;
632 }
633 if (i < count && str1[i]) return +1;
634 if (i < count && str2[i]) return -1;
635 return 0;
636 }
637
648 template <class T1, class T2>
649 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)
650 {
651 _Assume_(str1 || !count);
652 _Assume_(str2 || !count);
653 size_t i; T1 a; T2 b;
654 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
655 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
656 for (i = 0; i < count && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
657 if (a > b) return +1;
658 if (a < b) return -1;
659 }
660 if (i < count && str1[i]) return +1;
661 if (i < count && str2[i]) return -1;
662 return 0;
663 }
664
675 template <class T1, class T2>
676 inline int strnicmp(
677 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
678 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
679 {
680 _Assume_(str1 || !count1);
681 _Assume_(str2 || !count2);
682 size_t i; T1 a; T2 b;
683 for (i = 0; i < count1 && i < count2 && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); i++) {
684 if (a > b) return +1;
685 if (a < b) return -1;
686 }
687 if (i < count1 && str1[i]) return +1;
688 if (i < count2 && str2[i]) return -1;
689 return 0;
690 }
691
703 template <class T1, class T2>
704 inline int strnicmp(
705 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
706 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
707 _In_ const std::locale& locale)
708 {
709 _Assume_(str1 || !count1);
710 _Assume_(str2 || !count2);
711 size_t i; T1 a; T2 b;
712 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
713 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
714 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
715 if (a > b) return +1;
716 if (a < b) return -1;
717 }
718 if (i < count1 && str1[i]) return +1;
719 if (i < count2 && str2[i]) return -1;
720 return 0;
721 }
722
731 template <class T1, class T2>
732 inline size_t strstr(
733 _In_z_ const T1* str,
734 _In_z_ const T2* sample)
735 {
736 _Assume_(str);
737 _Assume_(sample);
738 for (size_t offset = 0;; ++offset) {
739 for (size_t i = offset, j = 0;; ++i, ++j) {
740 if (!sample[j])
741 return offset;
742 if (!str[i])
743 return npos;
744 if (str[i] != sample[j])
745 break;
746 }
747 }
748 }
749
759 template <class T1, class T2>
760 inline size_t strnstr(
761 _In_reads_or_z_opt_(count) const T1* str,
762 _In_ size_t count,
763 _In_z_ const T2* sample)
764 {
765 _Assume_(str || !count);
766 _Assume_(sample);
767 for (size_t offset = 0;; ++offset) {
768 for (size_t i = offset, j = 0;; ++i, ++j) {
769 if (!sample[j])
770 return offset;
771 if (i >= count || !str[i])
772 return npos;
773 if (str[i] != sample[j])
774 break;
775 }
776 }
777 }
778
787 template <class T1, class T2>
788 inline size_t stristr(
789 _In_z_ const T1* str,
790 _In_z_ const T2* sample)
791 {
792 _Assume_(str);
793 _Assume_(sample);
794 for (size_t offset = 0;; ++offset) {
795 for (size_t i = offset, j = 0;; ++i, ++j) {
796 if (!sample[j])
797 return offset;
798 if (!str[i])
799 return npos;
800 if (tolower(str[i]) != tolower(sample[j]))
801 break;
802 }
803 }
804 }
805
815 template <class T1, class T2>
816 inline size_t stristr(
817 _In_z_ const T1* str,
818 _In_z_ const T2* sample,
819 _In_ const std::locale& locale)
820 {
821 _Assume_(str);
822 _Assume_(sample);
823 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
824 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
825 for (size_t offset = 0;; ++offset) {
826 for (size_t i = offset, j = 0;; ++i, ++j) {
827 if (!sample[j])
828 return offset;
829 if (!str[i])
830 return npos;
831 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
832 break;
833 }
834 }
835 }
836
846 template <class T1, class T2>
847 inline size_t strnistr(
848 _In_reads_or_z_opt_(count) const T1* str,
849 _In_ size_t count,
850 _In_z_ const T2* sample)
851 {
852 _Assume_(str || !count);
853 _Assume_(sample);
854 for (size_t offset = 0;; ++offset) {
855 for (size_t i = offset, j = 0;; ++i, ++j) {
856 if (!sample[j])
857 return offset;
858 if (i >= count || !str[i])
859 return npos;
860 if (tolower(str[i]) != tolower(sample[j]))
861 break;
862 }
863 }
864 }
865
876 template <class T1, class T2>
877 inline size_t strnistr(
878 _In_reads_or_z_opt_(count) const T1* str,
879 _In_ size_t count,
880 _In_z_ const T2* sample,
881 _In_ const std::locale& locale)
882 {
883 _Assume_(str || !count);
884 _Assume_(sample);
885 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
886 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
887 for (size_t offset = 0;; ++offset) {
888 for (size_t i = offset, j = 0;; ++i, ++j) {
889 if (!sample[j])
890 return offset;
891 if (i >= count || !str[i])
892 return npos;
893 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
894 break;
895 }
896 }
897 }
898
907 template <class T1, class T2>
908 inline size_t strcpy(
909 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
910 _In_z_ const T2* src)
911 {
912 _Assume_(dst && src);
913 for (size_t i = 0; ; ++i) {
914 if ((dst[i] = src[i]) == 0)
915 return i;
916 }
917 }
918
928 template <class T1, class T2>
929 inline size_t strncpy(
930 _Out_writes_(count) _Post_maybez_ T1* dst,
931 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
932 {
933 _Assume_(dst && src || !count);
934 for (size_t i = 0; ; ++i) {
935 if (i >= count)
936 return i;
937 if ((dst[i] = src[i]) == 0)
938 return i;
939 }
940 }
941
952 template <class T1, class T2>
953 inline size_t strncpy(
954 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
955 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
956 {
957 _Assume_(dst || !count_dst);
958 _Assume_(src || !count_src);
959 for (size_t i = 0; ; ++i)
960 {
961 if (i >= count_dst)
962 return i;
963 if (i >= count_src) {
964 dst[i] = 0;
965 return i;
966 }
967 if ((dst[i] = src[i]) == 0)
968 return i;
969 }
970 }
971
980 template <class T1, class T2>
981 inline size_t strcat(
982 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
983 _In_z_ const T2* src)
984 {
985 _Assume_(dst && src);
986 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
987 if ((dst[j] = src[i]) == 0)
988 return j;
989 }
990 }
991
1001 template <class T1, class T2>
1002 inline size_t strncat(
1003 _Inout_z_ T1* dst,
1004 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1005 {
1006 _Assume_(dst && src || !count);
1007 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1008 if (i >= count)
1009 return j;
1010 if ((dst[j] = src[i]) == 0)
1011 return j;
1012 }
1013 }
1014
1025 template <class T1, class T2>
1026 inline size_t strncat(
1027 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1028 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1029 {
1030 _Assume_(dst || !count_dst);
1031 _Assume_(src || !count_src);
1032 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
1033 {
1034 if (j >= count_dst)
1035 return j;
1036 if (i >= count_src) {
1037 dst[j] = 0;
1038 return j;
1039 }
1040 if ((dst[j] = src[i]) == 0)
1041 return j;
1042 }
1043 }
1044
1055 template <class T>
1056 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
1057 {
1058 if (!str) _Unlikely_
1059 return nullptr;
1060 size_t count = strlen(str) + 1;
1061 T* dst = new T[count];
1062 strncpy(dst, count, str, SIZE_MAX);
1063 return dst;
1064 }
1065
1077 template <class T>
1078 inline _Ret_z_ T* strndup(
1079 _In_reads_or_z_opt_(count) const T* str,
1080 _In_ size_t count)
1081 {
1082 T* dst = new T[count];
1083 strncpy(dst, count, str, SIZE_MAX);
1084 return dst;
1085 }
1086
1096 template <class T>
1097 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
1098 {
1099 _Assume_(dst);
1100 _Assume_(src);
1101 size_t i, j;
1102 for (i = j = 0; src[j];) {
1103 if (src[j] != '\r' || src[j + 1] != '\n')
1104 dst[i++] = src[j++];
1105 else {
1106 dst[i++] = '\n';
1107 j += 2;
1108 }
1109 }
1110 dst[i] = 0;
1111 return i;
1112 }
1113
1120 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1121 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& dst, _In_z_ const _Elem* src)
1122 {
1123 _Assume_(src);
1124 _Assume_(src != dst.c_str());
1125 dst.clear();
1126 dst.reserve(strlen(src));
1127 for (size_t j = 0; src[j];) {
1128 if (src[j] != '\r' || src[j + 1] != '\n')
1129 dst += src[j++];
1130 else {
1131 dst += '\n';
1132 j += 2;
1133 }
1134 }
1135 }
1136
1142 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1143 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
1144 {
1145 size_t i, j, n;
1146 for (i = j = 0, n = str.size(); j < n;) {
1147 if (str[j] != '\r' || str[j + 1] != '\n')
1148 str[i++] = str[j++];
1149 else {
1150 str[i++] = '\n';
1151 j += 2;
1152 }
1153 }
1154 str.resize(i);
1155 }
1156
1158 template <class T, class T_bin>
1159 inline T_bin strtoint(
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 _Out_ uint8_t& flags)
1164 {
1165 _Assume_(str || !count);
1166 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
1167
1168 size_t i = 0;
1169 T_bin value = 0, digit,
1170 max_ui = (T_bin)-1,
1171 max_ui_pre1, max_ui_pre2;
1172
1173 flags = 0;
1174
1175 // Skip leading spaces.
1176 for (;; ++i) {
1177 if (i >= count || !str[i]) goto error;
1178 if (!isspace(str[i])) break;
1179 }
1180
1181 // Read the sign.
1182 if (str[i] == '+') {
1183 flags &= ~0x01;
1184 ++i;
1185 if (i >= count || !str[i]) goto error;
1186 }
1187 else if (str[i] == '-') {
1188 flags |= 0x01;
1189 ++i;
1190 if (i >= count || !str[i]) goto error;
1191 }
1192
1193 if (radix == 16) {
1194 // On hexadecimal, allow leading 0x.
1195 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
1196 i += 2;
1197 if (i >= count || !str[i]) goto error;
1198 }
1199 }
1200 else if (!radix) {
1201 // Autodetect radix.
1202 if (str[i] == '0') {
1203 ++i;
1204 if (i >= count || !str[i]) goto error;
1205 if (str[i] == 'x' || str[i] == 'X') {
1206 radix = 16;
1207 ++i;
1208 if (i >= count || !str[i]) goto error;
1209 }
1210 else
1211 radix = 8;
1212 }
1213 else
1214 radix = 10;
1215 }
1216
1217 // We have the radix.
1218 max_ui_pre1 = max_ui / (T_bin)radix;
1219 max_ui_pre2 = max_ui % (T_bin)radix;
1220 for (;;) {
1221 if ('0' <= str[i] && str[i] <= '9')
1222 digit = (T_bin)str[i] - '0';
1223 else if ('A' <= str[i] && str[i] <= 'Z')
1224 digit = (T_bin)str[i] - 'A' + '\x0a';
1225 else if ('a' <= str[i] && str[i] <= 'z')
1226 digit = (T_bin)str[i] - 'a' + '\x0a';
1227 else
1228 goto error;
1229 if (digit >= (T_bin)radix)
1230 goto error;
1231
1232 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
1233 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
1234 value = value * (T_bin)radix + digit;
1235 else {
1236 // Overflow!
1237 flags |= 0x02;
1238 }
1239
1240 ++i;
1241 if (i >= count || !str[i])
1242 goto error;
1243 }
1244
1245 error:
1246 if (end) *end = i;
1247 return value;
1248 }
1250
1261 template <class T, class T_bin>
1262 T_bin strtoint(
1263 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1264 _Out_opt_ size_t* end,
1265 _In_ int radix)
1266 {
1267 uint8_t flags;
1268 T_bin value;
1269
1270 switch (sizeof(T_bin)) {
1271 case 1:
1272 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
1273 if ((flags & 0x01) && (value & 0x80)) {
1274 // Sign bit is 1 => overflow.
1275 flags |= 0x02;
1276 }
1277 return (flags & 0x02) ?
1278 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
1279 (flags & 0x01) ? -value : value;
1280
1281 case 2:
1282 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
1283 if ((flags & 0x01) && (value & 0x8000)) {
1284 // Sign bit is 1 => overflow.
1285 flags |= 0x02;
1286 }
1287 return (flags & 0x02) ?
1288 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
1289 (flags & 0x01) ? -value : value;
1290
1291 case 4:
1292 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
1293 if ((flags & 0x01) && (value & 0x80000000)) {
1294 // Sign bit is 1 => overflow.
1295 flags |= 0x02;
1296 }
1297 return (flags & 0x02) ?
1298 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
1299 (flags & 0x01) ? -value : value;
1300
1301 case 8:
1302 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
1303 if ((flags & 0x01) && (value & 0x8000000000000000)) {
1304 // Sign bit is 1 => overflow.
1305 flags |= 0x02;
1306 }
1307 return (flags & 0x02) ?
1308 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
1309 (flags & 0x01) ? -value : value;
1310
1311 default:
1312 throw std::invalid_argument("Unsupported bit length");
1313 }
1314 }
1315
1326 template <class T, class T_bin>
1327 inline T_bin strtouint(
1328 _In_reads_or_z_opt_(count) const T* str,
1329 _In_ size_t count,
1330 _Out_opt_ size_t* end,
1331 _In_ int radix)
1332 {
1333 uint8_t flags;
1334 T_bin value;
1335
1336 switch (sizeof(T_bin)) {
1337 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
1338 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
1339 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
1340 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
1341 default: throw std::invalid_argument("Unsupported bit length");
1342 }
1343
1344 return (flags & 0x02) ?
1345 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
1346 (flags & 0x01) ? ~value : value;
1347 }
1348
1359 template <class T>
1360 inline int32_t strto32(
1361 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1362 _Out_opt_ size_t* end,
1363 _In_ int radix)
1364 {
1365 return strtoint<T, int32_t>(str, count, end, radix);
1366 }
1367
1378 template <class T>
1379 inline int64_t strto64(
1380 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1381 _Out_opt_ size_t* end,
1382 _In_ int radix)
1383 {
1384 return strtoint<T, int64_t>(str, count, end, radix);
1385 }
1386
1398 template <class T>
1399 inline intptr_t strtoi(
1400 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1401 _Out_opt_ size_t* end,
1402 _In_ int radix)
1403 {
1404#if defined(_WIN64) || defined(__LP64__)
1405 return (intptr_t)strto64(str, count, end, radix);
1406#else
1407 return (intptr_t)strto32(str, count, end, radix);
1408#endif
1409 }
1410
1421 template <class T>
1422 inline uint32_t strtou32(
1423 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1424 _Out_opt_ size_t* end,
1425 _In_ int radix)
1426 {
1427 return strtouint<T, uint32_t>(str, count, end, radix);
1428 }
1429
1440 template <class T>
1441 inline uint64_t strtou64(
1442 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1443 _Out_opt_ size_t* end,
1444 _In_ int radix)
1445 {
1446 return strtouint<T, uint64_t>(str, count, end, radix);
1447 }
1448
1460 template <class T>
1461 inline size_t strtoui(
1462 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1463 _Out_opt_ size_t* end,
1464 _In_ int radix)
1465 {
1466#if defined(_WIN64) || defined(__LP64__)
1467 return (size_t)strtou64(str, count, end, radix);
1468#else
1469 return (size_t)strtou32(str, count, end, radix);
1470#endif
1471 }
1472
1474 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)
1475 {
1476 int r;
1477#ifdef _WIN32
1478 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1479#pragma warning(suppress: 4996)
1480 r = _vsnprintf_l(str, capacity, format, locale, arg);
1481#else
1482 r = ::vsnprintf(str, capacity, format, arg);
1483#endif
1484 if (r == -1 && strnlen(str, capacity) == capacity) {
1485 // Buffer overrun. Estimate buffer size for the next iteration.
1486 capacity += std::max<size_t>(capacity / 8, 0x80);
1487 if (capacity > INT_MAX)
1488 throw std::invalid_argument("string too big");
1489 return (int)capacity;
1490 }
1491 return r;
1492 }
1493
1494 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)
1495 {
1496 int r;
1497#ifdef _WIN32
1498 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1499#pragma warning(suppress: 4996)
1500 r = _vsnwprintf_l(str, capacity, format, locale, arg);
1501#else
1502 r = vswprintf(str, capacity, format, arg);
1503#endif
1504 if (r == -1 && strnlen(str, capacity) == capacity) {
1505 // Buffer overrun. Estimate buffer size for the next iteration.
1506 capacity += std::max<size_t>(capacity / 8, 0x80);
1507 if (capacity > INT_MAX)
1508 throw std::invalid_argument("string too big");
1509 return (int)capacity;
1510 }
1511 return r;
1512 }
1514
1525 template<class _Elem, class _Traits, class _Ax>
1526 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)
1527 {
1528 _Elem buf[1024 / sizeof(_Elem)];
1529
1530 // Try with stack buffer first.
1531 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
1532 if (count >= 0) {
1533 // Copy from stack.
1534 str.append(buf, count);
1535 return count;
1536 }
1537 for (size_t capacity = 2 * 1024 / sizeof(_Elem);; capacity *= 2) {
1538 // Allocate on heap and retry.
1539 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1540 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
1541 if (count >= 0) {
1542 str.append(buf_dyn.get(), count);
1543 return count;
1544 }
1545 }
1546 }
1547
1557 template<class _Elem, class _Traits, class _Ax>
1558 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, ...)
1559 {
1560 va_list arg;
1561 va_start(arg, locale);
1562 size_t n = vappendf(str, format, locale, arg);
1563 va_end(arg);
1564 return n;
1565 }
1566
1575 template<class _Elem, class _Traits, class _Ax>
1576 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)
1577 {
1578 str.clear();
1579 vappendf(str, format, locale, arg);
1580 }
1581
1589 template<class _Elem, class _Traits, class _Ax>
1590 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, ...)
1591 {
1592 va_list arg;
1593 va_start(arg, locale);
1594 vsprintf(str, format, locale, arg);
1595 va_end(arg);
1596 }
1597
1607 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1608 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)
1609 {
1610 std::basic_string<_Elem, _Traits, _Ax> str;
1611 vappendf(str, format, locale, arg);
1612 return str;
1613 }
1614
1623 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1624 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem* format, _In_opt_ locale_t locale, ...)
1625 {
1626 va_list arg;
1627 va_start(arg, locale);
1628 auto str = vsprintf(format, locale, arg);
1629 va_end(arg);
1630 return str;
1631 }
1632
1634 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)
1635 {
1636#ifdef _WIN32
1637 return _strftime_l(str, capacity, format, time, locale);
1638#else
1639 return strftime_l(str, capacity, format, time, locale);
1640#endif
1641 }
1642
1643 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)
1644 {
1645#ifdef _WIN32
1646 return _wcsftime_l(str, capacity, format, time, locale);
1647#else
1648 return wcsftime_l(str, capacity, format, time, locale);
1649#endif
1650 }
1652
1661 template<class _Elem, class _Traits, class _Ax>
1662 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)
1663 {
1664 _Elem buf[1024 / sizeof(_Elem)];
1665
1666 // Try with stack buffer first.
1667 size_t count = strftime(buf, _countof(buf), format, time, locale);
1668 if (count) {
1669 // Copy from stack.
1670 str.append(buf, count);
1671 }
1672 else {
1673 for (size_t capacity = 2 * 1024 / sizeof(_Elem);; capacity *= 2) {
1674 // Allocate on heap and retry.
1675 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1676 count = strftime(buf_dyn.get(), capacity, format, time, locale);
1677 if (count) {
1678 str.append(buf_dyn.get(), count);
1679 break;
1680 }
1681 }
1682 }
1683 }
1684
1693 template<class _Elem, class _Traits, class _Ax>
1694 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)
1695 {
1696 str.clear();
1697 strcatftime(str, format, time, locale);
1698 }
1699
1710 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1711 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)
1712 {
1713 std::basic_string<_Elem, _Traits, _Ax> str;
1714 strcatftime(str, format, time, locale);
1715 return str;
1716 }
1717
1723 template<class T>
1724 inline void strlwr(_Inout_z_ T* str)
1725 {
1726 _Assume_(str);
1727 for (size_t i = 0; str[i]; ++i)
1728 str[i] = tolower(str[i]);
1729 }
1730
1737 template<class T>
1738 inline void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
1739 {
1740 _Assume_(str);
1741 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1742 for (size_t i = 0; str[i]; ++i)
1743 str[i] = ctype.tolower(str[i]);
1744 }
1745
1752 template<class T>
1753 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count)
1754 {
1755 _Assume_(str || !count);
1756 for (size_t i = 0; i < count && str[i]; ++i)
1757 str[i] = tolower(str[i]);
1758 }
1759
1767 template<class T>
1768 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1769 {
1770 _Assume_(str || !count);
1771 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1772 for (size_t i = 0; i < count && str[i]; ++i)
1773 str[i] = ctype.tolower(str[i]);
1774 }
1775
1781 template<class T>
1782 inline void strupr(_Inout_z_ T* str)
1783 {
1784 _Assume_(str);
1785 for (size_t i = 0; str[i]; ++i)
1786 str[i] = toupper(str[i]);
1787 }
1788
1795 template<class T>
1796 inline void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
1797 {
1798 _Assume_(str);
1799 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1800 for (size_t i = 0; str[i]; ++i)
1801 str[i] = ctype.toupper(str[i]);
1802 }
1803
1810 template<class T>
1811 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count)
1812 {
1813 _Assume_(str || !count);
1814 for (size_t i = 0; i < count && str[i]; ++i)
1815 str[i] = toupper(str[i]);
1816 }
1817
1825 template<class T>
1826 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1827 {
1828 _Assume_(str || !count);
1829 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1830 for (size_t i = 0; i < count && str[i]; ++i)
1831 str[i] = ctype.toupper(str[i]);
1832 }
1833
1839 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1840 inline void strupr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
1841 {
1842 for (size_t i = 0; i < str.size(); ++i)
1843 str[i] = toupper(str[i]);
1844 }
1845
1852 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1853 inline void strupr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str, _In_ const std::locale& locale)
1854 {
1855 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1856 for (size_t i = 0; i < str.size(); ++i)
1857 str[i] = ctype.toupper(str[i]);
1858 }
1859
1868 template<class T>
1869 inline size_t ltrim(
1870 _Inout_z_count_(count) T* str, _In_ size_t count)
1871 {
1872 for (size_t i = 0;; ++i) {
1873 if (i >= count) {
1874 if (count) str[0] = 0;
1875 return 0;
1876 }
1877 if (!str[i]) {
1878 str[0] = 0;
1879 return 0;
1880 }
1881 if (!isspace(str[i])) {
1882 if (!i)
1883 return strnlen(str, count);
1884 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
1885 str[n] = 0;
1886 return n;
1887 }
1888 }
1889 }
1890
1900 template<class T>
1901 inline size_t ltrim(
1902 _Inout_z_count_(count) T* str, _In_ size_t count,
1903 _In_ const std::locale& locale)
1904 {
1905 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1906 for (size_t i = 0;; ++i) {
1907 if (i >= count) {
1908 if (count) str[0] = 0;
1909 return 0;
1910 }
1911 if (!str[i]) {
1912 str[0] = 0;
1913 return 0;
1914 }
1915 if (!ctype.is(ctype.space, str[i])) {
1916 if (!i)
1917 return strnlen(str, count);
1918 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
1919 str[n] = 0;
1920 return n;
1921 }
1922 }
1923 }
1924
1930 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1931 inline void ltrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s)
1932 {
1933 s.erase(
1934 s.begin(),
1935 std::find_if(
1936 s.begin(),
1937 s.end(),
1938 [&](_In_ _Elem ch) { return !isspace(ch); }));
1939 }
1940
1947 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1948 inline void ltrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s, _In_ const std::locale& locale)
1949 {
1950 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1951 s.erase(
1952 s.begin(),
1953 std::find_if(
1954 s.begin(),
1955 s.end(),
1956 [&](_In_ _Elem ch) { return !ctype.is(ctype.space, ch); }));
1957 }
1958
1967 template<class T>
1968 inline size_t rtrim(
1969 _Inout_z_count_(count) T* str, _In_ size_t count)
1970 {
1971 for (size_t i = 0, j = 0;;) {
1972 if (i >= count || !str[i]) {
1973 if (j < count) str[j] = 0;
1974 return j;
1975 }
1976 if (!isspace(str[i]))
1977 j = ++i;
1978 else
1979 ++i;
1980 }
1981 }
1982
1992 template<class T>
1993 inline size_t rtrim(
1994 _Inout_z_count_(count) T* str, _In_ size_t count,
1995 _In_ const std::locale& locale)
1996 {
1997 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1998 for (size_t i = 0, j = 0;;) {
1999 if (i >= count || !str[i]) {
2000 if (j < count) str[j] = 0;
2001 return j;
2002 }
2003 if (!ctype.is(ctype.space, str[i]))
2004 j = ++i;
2005 else
2006 ++i;
2007 }
2008 }
2009
2015 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2016 static inline void rtrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s)
2017 {
2018 s.erase(
2019 std::find_if(
2020 s.rbegin(),
2021 s.rend(),
2022 [&](_In_ _Elem ch) { return !isspace(ch); }).base(),
2023 s.end());
2024 }
2025
2032 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2033 static inline void rtrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s, _In_ const std::locale& locale)
2034 {
2035 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
2036 s.erase(
2037 std::find_if(
2038 s.rbegin(),
2039 s.rend(),
2040 [&](_In_ _Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
2041 s.end());
2042 }
2043
2052 template<class T>
2053 inline size_t trim(
2054 _Inout_z_count_(count) T* str, _In_ size_t count)
2055 {
2056 return ltrim(str, rtrim(str, count));
2057 }
2058
2068 template<class T>
2069 inline size_t trim(
2070 _Inout_z_count_(count) T* str, _In_ size_t count,
2071 _In_ const std::locale& locale)
2072 {
2073 return ltrim(str, rtrim(str, count, locale), locale);
2074 }
2075
2081 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2082 static inline void trim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s)
2083 {
2084 auto nonspace = [&](_In_ _Elem ch) { return !isspace(ch); };
2085 s.erase(
2086 s.begin(),
2087 std::find_if(
2088 s.begin(),
2089 s.end(),
2090 nonspace));
2091 s.erase(
2092 std::find_if(
2093 s.rbegin(),
2094 s.rend(),
2095 nonspace).base(),
2096 s.end());
2097 }
2098
2105 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2106 static inline void trim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s, _In_ const std::locale& locale)
2107 {
2108 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
2109 auto nonspace = [&](_In_ _Elem ch) { return !ctype.is(ctype.space, ch); };
2110 s.erase(
2111 s.begin(),
2112 std::find_if(
2113 s.begin(),
2114 s.end(),
2115 nonspace));
2116 s.erase(
2117 std::find_if(
2118 s.rbegin(),
2119 s.rend(),
2120 nonspace).base(),
2121 s.end());
2122 }
2123}