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
186 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
187 {
188 _Assume_(glyph || !count);
189 if (count) {
190#ifdef _WIN32
191 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
192#else
193 size_t i = 1;
194#endif
195 for (; i < count && iscombining(glyph[i]); ++i);
196 return i;
197 }
198 return 0;
199 }
200
208 template <class T>
209 inline T tolower(_In_ T chr)
210 {
211 return isupper(chr) ? chr | 0x20 : chr;
212 }
213
221 template <class T>
222 inline T toupper(_In_ T chr)
223 {
224 return islower(chr) ? chr | ~0x20 : chr;
225 }
226
234 template <class T>
235 inline size_t strlen(_In_z_ const T* str)
236 {
237 _Assume_(str);
238 size_t i;
239 for (i = 0; str[i]; ++i);
240 return i;
241 }
242
251 template <class T>
252 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
253 {
254 _Assume_(str || !count);
255 size_t i;
256 for (i = 0; i < count && str[i]; ++i);
257 return i;
258 }
259
260 constexpr auto npos{ static_cast<size_t>(-1) };
261
270 template <class T>
271 inline size_t strchr(_In_z_ const T* str, _In_ T chr)
272 {
273 _Assume_(str);
274 for (size_t i = 0; str[i]; ++i)
275 if (str[i] == chr) return i;
276 return npos;
277 }
278
288 template <class T>
289 inline size_t strnchr(
290 _In_reads_or_z_opt_(count) const T* str,
291 _In_ size_t count,
292 _In_ T chr)
293 {
294 _Assume_(str || !count);
295 for (size_t i = 0; i < count && str[i]; ++i)
296 if (str[i] == chr) return i;
297 return npos;
298 }
299
309 template <class T>
310 inline size_t strrnchr(
311 _In_reads_or_z_opt_(count) const T* str,
312 _In_ size_t count,
313 _In_ T chr)
314 {
315 _Assume_(str || !count);
316 size_t z = npos;
317 for (size_t i = 0; i < count && str[i]; ++i)
318 if (str[i] == chr) z = i;
319 return z;
320 }
321
330 template <class T>
331 inline bool isblank(
332 _In_reads_or_z_opt_(count) const T* str,
333 _In_ size_t count)
334 {
335 _Assume_(str || !count);
336 for (size_t i = 0; i < count && str[i]; ++i)
337 if (!isspace(str[i]))
338 return false;
339 return true;
340 }
341
351 template <class T>
352 inline bool isblank(
353 _In_reads_or_z_opt_(count) const T* str,
354 _In_ size_t count,
355 _In_ const std::locale& locale)
356 {
357 _Assume_(str || !count);
358 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
359 for (size_t i = 0; i < count && str[i]; ++i)
360 if (!ctype.is(ctype.space, str[i]))
361 return false;
362 return true;
363 }
364
374 template <class T>
375 inline size_t strnichr(
376 _In_reads_or_z_opt_(count) const T* str,
377 _In_ size_t count,
378 _In_ T chr)
379 {
380 _Assume_(str || !count);
381 chr = tolower(chr);
382 for (size_t i = 0; i < count && str[i]; ++i)
383 if (tolower(str[i]) == chr) return i;
384 return npos;
385 }
386
397 template <class T>
398 inline size_t strnichr(
399 _In_reads_or_z_opt_(count) const T* str,
400 _In_ size_t count,
401 _In_ T chr,
402 _In_ const std::locale& locale)
403 {
404 _Assume_(str || !count);
405 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
406 chr = ctype.tolower(chr);
407 for (size_t i = 0; i < count && str[i]; ++i)
408 if (ctype.tolower(str[i]) == chr) return i;
409 return npos;
410 }
411
421 template <class T>
422 inline size_t strrnichr(
423 _In_reads_or_z_opt_(count) const T* str,
424 _In_ size_t count,
425 _In_ T chr)
426 {
427 _Assume_(str || !count);
428 chr = tolower(chr);
429 size_t z = npos;
430 for (size_t i = 0; i < count && str[i]; ++i)
431 if (tolower(str[i]) == chr) z = i;
432 return z;
433 }
434
445 template <class T>
446 inline size_t strrnichr(
447 _In_reads_or_z_opt_(count) const T* str,
448 _In_ size_t count,
449 _In_ T chr,
450 _In_ const std::locale& locale)
451 {
452 _Assume_(str || !count);
453 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
454 chr = ctype.tolower(chr);
455 size_t z = npos;
456 for (size_t i = 0; i < count && str[i]; ++i)
457 if (ctype.tolower(str[i]) == chr) z = i;
458 return z;
459 }
460
469 template <class T1, class T2>
470 inline int strcmp(const T1* str1, const T2* str2)
471 {
472 _Assume_(str1 && str2);
473 T1 a; T2 b;
474 for (size_t i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
475 if (a > b) return +1;
476 if (a < b) return -1;
477 }
478 return 0;
479 }
480
491 template <class T1, class T2>
492 inline int strncmp(
493 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
494 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
495 {
496 _Assume_(str1 || !count1);
497 _Assume_(str2 || !count2);
498 size_t i; T1 a; T2 b;
499 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
500 if (a > b) return +1;
501 if (a < b) return -1;
502 }
503 if (i < count1 && str1[i]) return +1;
504 if (i < count2 && str2[i]) return -1;
505 return 0;
506 }
507
517 template <class T1, class T2>
518 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)
519 {
520 _Assume_((str1 && str2) || !count);
521 size_t i; T1 a; T2 b;
522 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
523 if (a > b) return +1;
524 if (a < b) return -1;
525 }
526 if (i < count && str1[i]) return +1;
527 if (i < count && str2[i]) return -1;
528 return 0;
529 }
530
542 template <class T>
543 inline int strncoll(
544 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
545 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
546 _In_ const std::locale& locale)
547 {
548 _Assume_(str1 || !count1);
549 _Assume_(str2 || !count2);
550 auto& collate = std::use_facet<std::collate<T>>(locale);
551 return collate.compare(str1, str1 + count1, str2, str2 + count2);
552 }
553
562 template <class T1, class T2>
563 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
564 {
565 _Assume_(str1);
566 _Assume_(str2);
567 size_t i; T1 a; T2 b;
568 for (i = 0; (a = tolower(str1[i])) | (b = tolower(str2[i])); i++) {
569 if (a > b) return +1;
570 if (a < b) return -1;
571 }
572 if (str1[i]) return +1;
573 if (str2[i]) return -1;
574 return 0;
575 }
576
586 template <class T1, class T2>
587 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
588 {
589 _Assume_(str1);
590 _Assume_(str2);
591 size_t i; T1 a; T2 b;
592 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
593 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
594 for (i = 0; (a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i])); i++) {
595 if (a > b) return +1;
596 if (a < b) return -1;
597 }
598 if (str1[i]) return +1;
599 if (str2[i]) return -1;
600 return 0;
601 }
602
612 template <class T1, class T2>
613 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)
614 {
615 _Assume_(str1 || !count);
616 _Assume_(str2 || !count);
617 size_t i; T1 a; T2 b;
618 for (i = 0; i < count && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); i++) {
619 if (a > b) return +1;
620 if (a < b) return -1;
621 }
622 if (i < count && str1[i]) return +1;
623 if (i < count && str2[i]) return -1;
624 return 0;
625 }
626
637 template <class T1, class T2>
638 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)
639 {
640 _Assume_(str1 || !count);
641 _Assume_(str2 || !count);
642 size_t i; T1 a; T2 b;
643 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
644 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
645 for (i = 0; i < count && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
646 if (a > b) return +1;
647 if (a < b) return -1;
648 }
649 if (i < count && str1[i]) return +1;
650 if (i < count && str2[i]) return -1;
651 return 0;
652 }
653
664 template <class T1, class T2>
665 inline int strnicmp(
666 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
667 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
668 {
669 _Assume_(str1 || !count1);
670 _Assume_(str2 || !count2);
671 size_t i; T1 a; T2 b;
672 for (i = 0; i < count1 && i < count2 && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); i++) {
673 if (a > b) return +1;
674 if (a < b) return -1;
675 }
676 if (i < count1 && str1[i]) return +1;
677 if (i < count2 && str2[i]) return -1;
678 return 0;
679 }
680
692 template <class T1, class T2>
693 inline int strnicmp(
694 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
695 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
696 _In_ const std::locale& locale)
697 {
698 _Assume_(str1 || !count1);
699 _Assume_(str2 || !count2);
700 size_t i; T1 a; T2 b;
701 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
702 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
703 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
704 if (a > b) return +1;
705 if (a < b) return -1;
706 }
707 if (i < count1 && str1[i]) return +1;
708 if (i < count2 && str2[i]) return -1;
709 return 0;
710 }
711
720 template <class T1, class T2>
721 inline size_t strstr(
722 _In_z_ const T1* str,
723 _In_z_ const T2* sample)
724 {
725 _Assume_(str);
726 _Assume_(sample);
727 for (size_t offset = 0;; ++offset) {
728 for (size_t i = offset, j = 0;; ++i, ++j) {
729 if (!sample[j])
730 return offset;
731 if (!str[i])
732 return npos;
733 if (str[i] != sample[j])
734 break;
735 }
736 }
737 }
738
748 template <class T1, class T2>
749 inline size_t strnstr(
750 _In_reads_or_z_opt_(count) const T1* str,
751 _In_ size_t count,
752 _In_z_ const T2* sample)
753 {
754 _Assume_(str || !count);
755 _Assume_(sample);
756 for (size_t offset = 0;; ++offset) {
757 for (size_t i = offset, j = 0;; ++i, ++j) {
758 if (!sample[j])
759 return offset;
760 if (i >= count || !str[i])
761 return npos;
762 if (str[i] != sample[j])
763 break;
764 }
765 }
766 }
767
776 template <class T1, class T2>
777 inline size_t stristr(
778 _In_z_ const T1* str,
779 _In_z_ const T2* sample)
780 {
781 _Assume_(str);
782 _Assume_(sample);
783 for (size_t offset = 0;; ++offset) {
784 for (size_t i = offset, j = 0;; ++i, ++j) {
785 if (!sample[j])
786 return offset;
787 if (!str[i])
788 return npos;
789 if (tolower(str[i]) != tolower(sample[j]))
790 break;
791 }
792 }
793 }
794
804 template <class T1, class T2>
805 inline size_t stristr(
806 _In_z_ const T1* str,
807 _In_z_ const T2* sample,
808 _In_ const std::locale& locale)
809 {
810 _Assume_(str);
811 _Assume_(sample);
812 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
813 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
814 for (size_t offset = 0;; ++offset) {
815 for (size_t i = offset, j = 0;; ++i, ++j) {
816 if (!sample[j])
817 return offset;
818 if (!str[i])
819 return npos;
820 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
821 break;
822 }
823 }
824 }
825
835 template <class T1, class T2>
836 inline size_t strnistr(
837 _In_reads_or_z_opt_(count) const T1* str,
838 _In_ size_t count,
839 _In_z_ const T2* sample)
840 {
841 _Assume_(str || !count);
842 _Assume_(sample);
843 for (size_t offset = 0;; ++offset) {
844 for (size_t i = offset, j = 0;; ++i, ++j) {
845 if (!sample[j])
846 return offset;
847 if (i >= count || !str[i])
848 return npos;
849 if (tolower(str[i]) != tolower(sample[j]))
850 break;
851 }
852 }
853 }
854
865 template <class T1, class T2>
866 inline size_t strnistr(
867 _In_reads_or_z_opt_(count) const T1* str,
868 _In_ size_t count,
869 _In_z_ const T2* sample,
870 _In_ const std::locale& locale)
871 {
872 _Assume_(str || !count);
873 _Assume_(sample);
874 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
875 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
876 for (size_t offset = 0;; ++offset) {
877 for (size_t i = offset, j = 0;; ++i, ++j) {
878 if (!sample[j])
879 return offset;
880 if (i >= count || !str[i])
881 return npos;
882 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
883 break;
884 }
885 }
886 }
887
896 template <class T1, class T2>
897 inline size_t strcpy(
898 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
899 _In_z_ const T2* src)
900 {
901 _Assume_(dst && src);
902 for (size_t i = 0; ; ++i) {
903 if ((dst[i] = src[i]) == 0)
904 return i;
905 }
906 }
907
917 template <class T1, class T2>
918 inline size_t strncpy(
919 _Out_writes_(count) _Post_maybez_ T1* dst,
920 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
921 {
922 _Assume_(dst && src || !count);
923 for (size_t i = 0; ; ++i) {
924 if (i >= count)
925 return i;
926 if ((dst[i] = src[i]) == 0)
927 return i;
928 }
929 }
930
941 template <class T1, class T2>
942 inline size_t strncpy(
943 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
944 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
945 {
946 _Assume_(dst || !count_dst);
947 _Assume_(src || !count_src);
948 for (size_t i = 0; ; ++i)
949 {
950 if (i >= count_dst)
951 return i;
952 if (i >= count_src) {
953 dst[i] = 0;
954 return i;
955 }
956 if ((dst[i] = src[i]) == 0)
957 return i;
958 }
959 }
960
969 template <class T1, class T2>
970 inline size_t strcat(
971 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
972 _In_z_ const T2* src)
973 {
974 _Assume_(dst && src);
975 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
976 if ((dst[j] = src[i]) == 0)
977 return j;
978 }
979 }
980
990 template <class T1, class T2>
991 inline size_t strncat(
992 _Inout_z_ T1* dst,
993 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
994 {
995 _Assume_(dst && src || !count);
996 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
997 if (i >= count)
998 return j;
999 if ((dst[j] = src[i]) == 0)
1000 return j;
1001 }
1002 }
1003
1014 template <class T1, class T2>
1015 inline size_t strncat(
1016 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1017 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1018 {
1019 _Assume_(dst || !count_dst);
1020 _Assume_(src || !count_src);
1021 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
1022 {
1023 if (j >= count_dst)
1024 return j;
1025 if (i >= count_src) {
1026 dst[j] = 0;
1027 return j;
1028 }
1029 if ((dst[j] = src[i]) == 0)
1030 return j;
1031 }
1032 }
1033
1044 template <class T>
1045 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
1046 {
1047 if (!str) _Unlikely_
1048 return nullptr;
1049 size_t count = strlen(str) + 1;
1050 T* dst = new T[count];
1051 strncpy(dst, count, str, SIZE_MAX);
1052 return dst;
1053 }
1054
1066 template <class T>
1067 inline _Ret_z_ T* strndup(
1068 _In_reads_or_z_opt_(count) const T* str,
1069 _In_ size_t count)
1070 {
1071 T* dst = new T[count];
1072 strncpy(dst, count, str, SIZE_MAX);
1073 return dst;
1074 }
1075
1085 template <class T>
1086 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
1087 {
1088 _Assume_(dst);
1089 _Assume_(src);
1090 size_t i, j;
1091 for (i = j = 0; src[j];) {
1092 if (src[j] != '\r' || src[j + 1] != '\n')
1093 dst[i++] = src[j++];
1094 else {
1095 dst[i++] = '\n';
1096 j += 2;
1097 }
1098 }
1099 dst[i] = 0;
1100 return i;
1101 }
1102
1109 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1110 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& dst, _In_z_ const _Elem* src)
1111 {
1112 _Assume_(src);
1113 _Assume_(src != dst.c_str());
1114 dst.clear();
1115 dst.reserve(strlen(src));
1116 for (size_t j = 0; src[j];) {
1117 if (src[j] != '\r' || src[j + 1] != '\n')
1118 dst += src[j++];
1119 else {
1120 dst += '\n';
1121 j += 2;
1122 }
1123 }
1124 }
1125
1131 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1132 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
1133 {
1134 size_t i, j, n;
1135 for (i = j = 0, n = str.size(); j < n;) {
1136 if (str[j] != '\r' || str[j + 1] != '\n')
1137 str[i++] = str[j++];
1138 else {
1139 str[i++] = '\n';
1140 j += 2;
1141 }
1142 }
1143 str.resize(i);
1144 }
1145
1147 template <class T, class T_bin>
1148 inline T_bin strtoint(
1149 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1150 _Out_opt_ size_t* end,
1151 _In_ int radix,
1152 _Out_ uint8_t& flags)
1153 {
1154 _Assume_(str || !count);
1155 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
1156
1157 size_t i = 0;
1158 T_bin value = 0, digit,
1159 max_ui = (T_bin)-1,
1160 max_ui_pre1, max_ui_pre2;
1161
1162 flags = 0;
1163
1164 // Skip leading spaces.
1165 for (;; ++i) {
1166 if (i >= count || !str[i]) goto error;
1167 if (!isspace(str[i])) break;
1168 }
1169
1170 // Read the sign.
1171 if (str[i] == '+') {
1172 flags &= ~0x01;
1173 ++i;
1174 if (i >= count || !str[i]) goto error;
1175 }
1176 else if (str[i] == '-') {
1177 flags |= 0x01;
1178 ++i;
1179 if (i >= count || !str[i]) goto error;
1180 }
1181
1182 if (radix == 16) {
1183 // On hexadecimal, allow leading 0x.
1184 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
1185 i += 2;
1186 if (i >= count || !str[i]) goto error;
1187 }
1188 }
1189 else if (!radix) {
1190 // Autodetect radix.
1191 if (str[i] == '0') {
1192 ++i;
1193 if (i >= count || !str[i]) goto error;
1194 if (str[i] == 'x' || str[i] == 'X') {
1195 radix = 16;
1196 ++i;
1197 if (i >= count || !str[i]) goto error;
1198 }
1199 else
1200 radix = 8;
1201 }
1202 else
1203 radix = 10;
1204 }
1205
1206 // We have the radix.
1207 max_ui_pre1 = max_ui / (T_bin)radix;
1208 max_ui_pre2 = max_ui % (T_bin)radix;
1209 for (;;) {
1210 if ('0' <= str[i] && str[i] <= '9')
1211 digit = (T_bin)str[i] - '0';
1212 else if ('A' <= str[i] && str[i] <= 'Z')
1213 digit = (T_bin)str[i] - 'A' + '\x0a';
1214 else if ('a' <= str[i] && str[i] <= 'z')
1215 digit = (T_bin)str[i] - 'a' + '\x0a';
1216 else
1217 goto error;
1218 if (digit >= (T_bin)radix)
1219 goto error;
1220
1221 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
1222 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
1223 value = value * (T_bin)radix + digit;
1224 else {
1225 // Overflow!
1226 flags |= 0x02;
1227 }
1228
1229 ++i;
1230 if (i >= count || !str[i])
1231 goto error;
1232 }
1233
1234 error:
1235 if (end) *end = i;
1236 return value;
1237 }
1239
1250 template <class T, class T_bin>
1251 T_bin strtoint(
1252 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1253 _Out_opt_ size_t* end,
1254 _In_ int radix)
1255 {
1256 uint8_t flags;
1257 T_bin value;
1258
1259 switch (sizeof(T_bin)) {
1260 case 1:
1261 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
1262 if ((flags & 0x01) && (value & 0x80)) {
1263 // Sign bit is 1 => overflow.
1264 flags |= 0x02;
1265 }
1266 return (flags & 0x02) ?
1267 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
1268 (flags & 0x01) ? -value : value;
1269
1270 case 2:
1271 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
1272 if ((flags & 0x01) && (value & 0x8000)) {
1273 // Sign bit is 1 => overflow.
1274 flags |= 0x02;
1275 }
1276 return (flags & 0x02) ?
1277 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
1278 (flags & 0x01) ? -value : value;
1279
1280 case 4:
1281 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
1282 if ((flags & 0x01) && (value & 0x80000000)) {
1283 // Sign bit is 1 => overflow.
1284 flags |= 0x02;
1285 }
1286 return (flags & 0x02) ?
1287 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
1288 (flags & 0x01) ? -value : value;
1289
1290 case 8:
1291 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
1292 if ((flags & 0x01) && (value & 0x8000000000000000)) {
1293 // Sign bit is 1 => overflow.
1294 flags |= 0x02;
1295 }
1296 return (flags & 0x02) ?
1297 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
1298 (flags & 0x01) ? -value : value;
1299
1300 default:
1301 throw std::invalid_argument("Unsupported bit length");
1302 }
1303 }
1304
1315 template <class T, class T_bin>
1316 inline T_bin strtouint(
1317 _In_reads_or_z_opt_(count) const T* str,
1318 _In_ size_t count,
1319 _Out_opt_ size_t* end,
1320 _In_ int radix)
1321 {
1322 uint8_t flags;
1323 T_bin value;
1324
1325 switch (sizeof(T_bin)) {
1326 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
1327 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
1328 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
1329 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
1330 default: throw std::invalid_argument("Unsupported bit length");
1331 }
1332
1333 return (flags & 0x02) ?
1334 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
1335 (flags & 0x01) ? ~value : value;
1336 }
1337
1348 template <class T>
1349 inline int32_t strto32(
1350 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1351 _Out_opt_ size_t* end,
1352 _In_ int radix)
1353 {
1354 return strtoint<T, int32_t>(str, count, end, radix);
1355 }
1356
1367 template <class T>
1368 inline int64_t strto64(
1369 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1370 _Out_opt_ size_t* end,
1371 _In_ int radix)
1372 {
1373 return strtoint<T, int64_t>(str, count, end, radix);
1374 }
1375
1387 template <class T>
1388 inline intptr_t strtoi(
1389 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1390 _Out_opt_ size_t* end,
1391 _In_ int radix)
1392 {
1393#if defined(_WIN64) || defined(__LP64__)
1394 return (intptr_t)strto64(str, count, end, radix);
1395#else
1396 return (intptr_t)strto32(str, count, end, radix);
1397#endif
1398 }
1399
1410 template <class T>
1411 inline uint32_t strtou32(
1412 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1413 _Out_opt_ size_t* end,
1414 _In_ int radix)
1415 {
1416 return strtouint<T, uint32_t>(str, count, end, radix);
1417 }
1418
1429 template <class T>
1430 inline uint64_t strtou64(
1431 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1432 _Out_opt_ size_t* end,
1433 _In_ int radix)
1434 {
1435 return strtouint<T, uint64_t>(str, count, end, radix);
1436 }
1437
1449 template <class T>
1450 inline size_t strtoui(
1451 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1452 _Out_opt_ size_t* end,
1453 _In_ int radix)
1454 {
1455#if defined(_WIN64) || defined(__LP64__)
1456 return (size_t)strtou64(str, count, end, radix);
1457#else
1458 return (size_t)strtou32(str, count, end, radix);
1459#endif
1460 }
1461
1463 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)
1464 {
1465 int r;
1466#ifdef _WIN32
1467 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1468#pragma warning(suppress: 4996)
1469 r = _vsnprintf_l(str, capacity, format, locale, arg);
1470#else
1471 r = ::vsnprintf(str, capacity, format, arg);
1472#endif
1473 if (r == -1 && strnlen(str, capacity) == capacity) {
1474 // Buffer overrun. Estimate buffer size for the next iteration.
1475 capacity += std::max<size_t>(capacity / 8, 0x80);
1476 if (capacity > INT_MAX)
1477 throw std::invalid_argument("string too big");
1478 return (int)capacity;
1479 }
1480 return r;
1481 }
1482
1483 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)
1484 {
1485 int r;
1486#ifdef _WIN32
1487 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1488#pragma warning(suppress: 4996)
1489 r = _vsnwprintf_l(str, capacity, format, locale, arg);
1490#else
1491 r = vswprintf(str, capacity, format, arg);
1492#endif
1493 if (r == -1 && strnlen(str, capacity) == capacity) {
1494 // Buffer overrun. Estimate buffer size for the next iteration.
1495 capacity += std::max<size_t>(capacity / 8, 0x80);
1496 if (capacity > INT_MAX)
1497 throw std::invalid_argument("string too big");
1498 return (int)capacity;
1499 }
1500 return r;
1501 }
1503
1514 template<class _Elem, class _Traits, class _Ax>
1515 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)
1516 {
1517 _Elem buf[1024 / sizeof(_Elem)];
1518
1519 // Try with stack buffer first.
1520 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
1521 if (count >= 0) {
1522 // Copy from stack.
1523 str.append(buf, count);
1524 return count;
1525 }
1526 for (size_t capacity = 2 * 1024 / sizeof(_Elem);; capacity *= 2) {
1527 // Allocate on heap and retry.
1528 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1529 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
1530 if (count >= 0) {
1531 str.append(buf_dyn.get(), count);
1532 return count;
1533 }
1534 }
1535 }
1536
1546 template<class _Elem, class _Traits, class _Ax>
1547 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, ...)
1548 {
1549 va_list arg;
1550 va_start(arg, locale);
1551 size_t n = vappendf(str, format, locale, arg);
1552 va_end(arg);
1553 return n;
1554 }
1555
1564 template<class _Elem, class _Traits, class _Ax>
1565 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)
1566 {
1567 str.clear();
1568 vappendf(str, format, locale, arg);
1569 }
1570
1578 template<class _Elem, class _Traits, class _Ax>
1579 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, ...)
1580 {
1581 va_list arg;
1582 va_start(arg, locale);
1583 vsprintf(str, format, locale, arg);
1584 va_end(arg);
1585 }
1586
1596 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1597 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)
1598 {
1599 std::basic_string<_Elem, _Traits, _Ax> str;
1600 vappendf(str, format, locale, arg);
1601 return str;
1602 }
1603
1612 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1613 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem* format, _In_opt_ locale_t locale, ...)
1614 {
1615 va_list arg;
1616 va_start(arg, locale);
1617 auto str = vsprintf(format, locale, arg);
1618 va_end(arg);
1619 return str;
1620 }
1621
1623 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)
1624 {
1625#ifdef _WIN32
1626 return _strftime_l(str, capacity, format, time, locale);
1627#else
1628 return strftime_l(str, capacity, format, time, locale);
1629#endif
1630 }
1631
1632 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)
1633 {
1634#ifdef _WIN32
1635 return _wcsftime_l(str, capacity, format, time, locale);
1636#else
1637 return wcsftime_l(str, capacity, format, time, locale);
1638#endif
1639 }
1641
1650 template<class _Elem, class _Traits, class _Ax>
1651 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)
1652 {
1653 _Elem buf[1024 / sizeof(_Elem)];
1654
1655 // Try with stack buffer first.
1656 size_t count = strftime(buf, _countof(buf), format, time, locale);
1657 if (count) {
1658 // Copy from stack.
1659 str.append(buf, count);
1660 }
1661 else {
1662 for (size_t capacity = 2 * 1024 / sizeof(_Elem);; capacity *= 2) {
1663 // Allocate on heap and retry.
1664 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1665 count = strftime(buf_dyn.get(), capacity, format, time, locale);
1666 if (count) {
1667 str.append(buf_dyn.get(), count);
1668 break;
1669 }
1670 }
1671 }
1672 }
1673
1682 template<class _Elem, class _Traits, class _Ax>
1683 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)
1684 {
1685 str.clear();
1686 strcatftime(str, format, time, locale);
1687 }
1688
1699 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1700 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)
1701 {
1702 std::basic_string<_Elem, _Traits, _Ax> str;
1703 strcatftime(str, format, time, locale);
1704 return str;
1705 }
1706
1712 template<class T>
1713 inline void strlwr(_Inout_z_ T* str)
1714 {
1715 _Assume_(str);
1716 for (size_t i = 0; str[i]; ++i)
1717 str[i] = tolower(str[i]);
1718 }
1719
1726 template<class T>
1727 inline void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
1728 {
1729 _Assume_(str);
1730 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1731 for (size_t i = 0; str[i]; ++i)
1732 str[i] = ctype.tolower(str[i]);
1733 }
1734
1741 template<class T>
1742 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count)
1743 {
1744 _Assume_(str || !count);
1745 for (size_t i = 0; i < count && str[i]; ++i)
1746 str[i] = tolower(str[i]);
1747 }
1748
1756 template<class T>
1757 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1758 {
1759 _Assume_(str || !count);
1760 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1761 for (size_t i = 0; i < count && str[i]; ++i)
1762 str[i] = ctype.tolower(str[i]);
1763 }
1764
1770 template<class T>
1771 inline void strupr(_Inout_z_ T* str)
1772 {
1773 _Assume_(str);
1774 for (size_t i = 0; str[i]; ++i)
1775 str[i] = toupper(str[i]);
1776 }
1777
1784 template<class T>
1785 inline void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
1786 {
1787 _Assume_(str);
1788 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1789 for (size_t i = 0; str[i]; ++i)
1790 str[i] = ctype.toupper(str[i]);
1791 }
1792
1799 template<class T>
1800 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count)
1801 {
1802 _Assume_(str || !count);
1803 for (size_t i = 0; i < count && str[i]; ++i)
1804 str[i] = toupper(str[i]);
1805 }
1806
1814 template<class T>
1815 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1816 {
1817 _Assume_(str || !count);
1818 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1819 for (size_t i = 0; i < count && str[i]; ++i)
1820 str[i] = ctype.toupper(str[i]);
1821 }
1822
1828 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1829 inline void strupr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
1830 {
1831 for (size_t i = 0; i < str.size(); ++i)
1832 str[i] = toupper(str[i]);
1833 }
1834
1841 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1842 inline void strupr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str, _In_ const std::locale& locale)
1843 {
1844 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1845 for (size_t i = 0; i < str.size(); ++i)
1846 str[i] = ctype.toupper(str[i]);
1847 }
1848
1857 template<class T>
1858 inline size_t ltrim(
1859 _Inout_z_count_(count) T* str, _In_ size_t count)
1860 {
1861 for (size_t i = 0;; ++i) {
1862 if (i >= count) {
1863 if (count) str[0] = 0;
1864 return 0;
1865 }
1866 if (!str[i]) {
1867 str[0] = 0;
1868 return 0;
1869 }
1870 if (!isspace(str[i])) {
1871 if (!i)
1872 return strnlen(str, count);
1873 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
1874 str[n] = 0;
1875 return n;
1876 }
1877 }
1878 }
1879
1889 template<class T>
1890 inline size_t ltrim(
1891 _Inout_z_count_(count) T* str, _In_ size_t count,
1892 _In_ const std::locale& locale)
1893 {
1894 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1895 for (size_t i = 0;; ++i) {
1896 if (i >= count) {
1897 if (count) str[0] = 0;
1898 return 0;
1899 }
1900 if (!str[i]) {
1901 str[0] = 0;
1902 return 0;
1903 }
1904 if (!ctype.is(ctype.space, str[i])) {
1905 if (!i)
1906 return strnlen(str, count);
1907 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
1908 str[n] = 0;
1909 return n;
1910 }
1911 }
1912 }
1913
1919 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1920 inline void ltrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s)
1921 {
1922 s.erase(
1923 s.begin(),
1924 std::find_if(
1925 s.begin(),
1926 s.end(),
1927 [&](_In_ _Elem ch) { return !isspace(ch); }));
1928 }
1929
1936 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1937 inline void ltrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s, _In_ const std::locale& locale)
1938 {
1939 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1940 s.erase(
1941 s.begin(),
1942 std::find_if(
1943 s.begin(),
1944 s.end(),
1945 [&](_In_ _Elem ch) { return !ctype.is(ctype.space, ch); }));
1946 }
1947
1956 template<class T>
1957 inline size_t rtrim(
1958 _Inout_z_count_(count) T* str, _In_ size_t count)
1959 {
1960 for (size_t i = 0, j = 0;;) {
1961 if (i >= count || !str[i]) {
1962 if (j < count) str[j] = 0;
1963 return j;
1964 }
1965 if (!isspace(str[i]))
1966 j = ++i;
1967 else
1968 ++i;
1969 }
1970 }
1971
1981 template<class T>
1982 inline size_t rtrim(
1983 _Inout_z_count_(count) T* str, _In_ size_t count,
1984 _In_ const std::locale& locale)
1985 {
1986 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1987 for (size_t i = 0, j = 0;;) {
1988 if (i >= count || !str[i]) {
1989 if (j < count) str[j] = 0;
1990 return j;
1991 }
1992 if (!ctype.is(ctype.space, str[i]))
1993 j = ++i;
1994 else
1995 ++i;
1996 }
1997 }
1998
2004 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2005 static inline void rtrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s)
2006 {
2007 s.erase(
2008 std::find_if(
2009 s.rbegin(),
2010 s.rend(),
2011 [&](_In_ _Elem ch) { return !isspace(ch); }).base(),
2012 s.end());
2013 }
2014
2021 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2022 static inline void rtrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s, _In_ const std::locale& locale)
2023 {
2024 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
2025 s.erase(
2026 std::find_if(
2027 s.rbegin(),
2028 s.rend(),
2029 [&](_In_ _Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
2030 s.end());
2031 }
2032
2041 template<class T>
2042 inline size_t trim(
2043 _Inout_z_count_(count) T* str, _In_ size_t count)
2044 {
2045 return ltrim(str, rtrim(str, count));
2046 }
2047
2057 template<class T>
2058 inline size_t trim(
2059 _Inout_z_count_(count) T* str, _In_ size_t count,
2060 _In_ const std::locale& locale)
2061 {
2062 return ltrim(str, rtrim(str, count, locale), locale);
2063 }
2064
2070 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2071 static inline void trim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s)
2072 {
2073 auto nonspace = [&](_In_ _Elem ch) { return !isspace(ch); };
2074 s.erase(
2075 s.begin(),
2076 std::find_if(
2077 s.begin(),
2078 s.end(),
2079 nonspace));
2080 s.erase(
2081 std::find_if(
2082 s.rbegin(),
2083 s.rend(),
2084 nonspace).base(),
2085 s.end());
2086 }
2087
2094 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2095 static inline void trim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s, _In_ const std::locale& locale)
2096 {
2097 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
2098 auto nonspace = [&](_In_ _Elem ch) { return !ctype.is(ctype.space, ch); };
2099 s.erase(
2100 s.begin(),
2101 std::find_if(
2102 s.begin(),
2103 s.end(),
2104 nonspace));
2105 s.erase(
2106 std::find_if(
2107 s.rbegin(),
2108 s.rend(),
2109 nonspace).base(),
2110 s.end());
2111 }
2112}