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
278 template <class T, size_t SIZE>
279 inline size_t strnlen(_In_ const T (&str)[SIZE])
280 {
281 return strnlen(str, SIZE);
282 }
283
284 constexpr auto npos{ static_cast<size_t>(-1) };
285
294 template <class T>
295 inline size_t strchr(_In_z_ const T* str, _In_ T chr)
296 {
297 _Assume_(str);
298 for (size_t i = 0; str[i]; ++i)
299 if (str[i] == chr) return i;
300 return npos;
301 }
302
311 template <class T, class _Traits = std::char_traits<T>, class _Ax = std::allocator<T>>
312 inline size_t strrchr(_In_ const std::basic_string<T, _Traits, _Ax>& str, _In_ T chr)
313 {
314 return strrnchr(str.data(), str.size(), chr);
315 }
316
326 template <class T>
327 inline size_t strnchr(
328 _In_reads_or_z_opt_(count) const T* str,
329 _In_ size_t count,
330 _In_ T chr)
331 {
332 _Assume_(str || !count);
333 for (size_t i = 0; i < count && str[i]; ++i)
334 if (str[i] == chr) return i;
335 return npos;
336 }
337
347 template <class T>
348 inline size_t strrnchr(
349 _In_reads_or_z_opt_(count) const T* str,
350 _In_ size_t count,
351 _In_ T chr)
352 {
353 _Assume_(str || !count);
354 size_t z = npos;
355 for (size_t i = 0; i < count && str[i]; ++i)
356 if (str[i] == chr) z = i;
357 return z;
358 }
359
368 template <class T>
369 inline bool isblank(
370 _In_reads_or_z_opt_(count) const T* str,
371 _In_ size_t count)
372 {
373 _Assume_(str || !count);
374 for (size_t i = 0; i < count && str[i]; ++i)
375 if (!isspace(str[i]))
376 return false;
377 return true;
378 }
379
389 template <class T>
390 inline bool isblank(
391 _In_reads_or_z_opt_(count) const T* str,
392 _In_ size_t count,
393 _In_ const std::locale& locale)
394 {
395 _Assume_(str || !count);
396 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
397 for (size_t i = 0; i < count && str[i]; ++i)
398 if (!ctype.is(ctype.space, str[i]))
399 return false;
400 return true;
401 }
402
412 template <class T>
413 inline size_t strnichr(
414 _In_reads_or_z_opt_(count) const T* str,
415 _In_ size_t count,
416 _In_ T chr)
417 {
418 _Assume_(str || !count);
419 chr = tolower(chr);
420 for (size_t i = 0; i < count && str[i]; ++i)
421 if (tolower(str[i]) == chr) return i;
422 return npos;
423 }
424
435 template <class T>
436 inline size_t strnichr(
437 _In_reads_or_z_opt_(count) const T* str,
438 _In_ size_t count,
439 _In_ T chr,
440 _In_ const std::locale& locale)
441 {
442 _Assume_(str || !count);
443 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
444 chr = ctype.tolower(chr);
445 for (size_t i = 0; i < count && str[i]; ++i)
446 if (ctype.tolower(str[i]) == chr) return i;
447 return npos;
448 }
449
459 template <class T>
460 inline size_t strrnichr(
461 _In_reads_or_z_opt_(count) const T* str,
462 _In_ size_t count,
463 _In_ T chr)
464 {
465 _Assume_(str || !count);
466 chr = tolower(chr);
467 size_t z = npos;
468 for (size_t i = 0; i < count && str[i]; ++i)
469 if (tolower(str[i]) == chr) z = i;
470 return z;
471 }
472
483 template <class T>
484 inline size_t strrnichr(
485 _In_reads_or_z_opt_(count) const T* str,
486 _In_ size_t count,
487 _In_ T chr,
488 _In_ const std::locale& locale)
489 {
490 _Assume_(str || !count);
491 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
492 chr = ctype.tolower(chr);
493 size_t z = npos;
494 for (size_t i = 0; i < count && str[i]; ++i)
495 if (ctype.tolower(str[i]) == chr) z = i;
496 return z;
497 }
498
507 template <class T1, class T2>
508 inline int strcmp(const T1* str1, const T2* str2)
509 {
510 _Assume_(str1 && str2);
511 T1 a; T2 b;
512 for (size_t i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
513 if (a > b) return +1;
514 if (a < b) return -1;
515 }
516 return 0;
517 }
518
529 template <class T1, class T2>
530 inline int strncmp(
531 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
532 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
533 {
534 _Assume_(str1 || !count1);
535 _Assume_(str2 || !count2);
536 size_t i; T1 a; T2 b;
537 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
538 if (a > b) return +1;
539 if (a < b) return -1;
540 }
541 if (i < count1 && str1[i]) return +1;
542 if (i < count2 && str2[i]) return -1;
543 return 0;
544 }
545
555 template <class T1, class T2>
556 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)
557 {
558 _Assume_((str1 && str2) || !count);
559 size_t i; T1 a; T2 b;
560 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
561 if (a > b) return +1;
562 if (a < b) return -1;
563 }
564 if (i < count && str1[i]) return +1;
565 if (i < count && str2[i]) return -1;
566 return 0;
567 }
568
580 template <class T>
581 inline int strncoll(
582 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
583 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
584 _In_ const std::locale& locale)
585 {
586 _Assume_(str1 || !count1);
587 _Assume_(str2 || !count2);
588 auto& collate = std::use_facet<std::collate<T>>(locale);
589 return collate.compare(str1, str1 + count1, str2, str2 + count2);
590 }
591
600 template <class T1, class T2>
601 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2)
602 {
603 _Assume_(str1);
604 _Assume_(str2);
605 size_t i; T1 a; T2 b;
606 for (i = 0; (a = tolower(str1[i])) | (b = tolower(str2[i])); i++) {
607 if (a > b) return +1;
608 if (a < b) return -1;
609 }
610 if (str1[i]) return +1;
611 if (str2[i]) return -1;
612 return 0;
613 }
614
623 template <class T1, class _Traits1 = std::char_traits<T1>, class _Ax1 = std::allocator<T1>, class T2, class _Traits2 = std::char_traits<T2>, class _Ax2 = std::allocator<T2>>
624 inline int stricmp(
625 _In_ const std::basic_string<T1, _Traits1, _Ax1>& str1,
626 _In_ const std::basic_string<T2, _Traits2, _Ax2>& str2)
627 {
628 return strnicmp(str1.data(), str1.size(), str2.data(), str2.size());
629 }
630
640 template <class T1, class T2>
641 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
642 {
643 _Assume_(str1);
644 _Assume_(str2);
645 size_t i; T1 a; T2 b;
646 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
647 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
648 for (i = 0; (a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i])); i++) {
649 if (a > b) return +1;
650 if (a < b) return -1;
651 }
652 if (str1[i]) return +1;
653 if (str2[i]) return -1;
654 return 0;
655 }
656
666 template <class T1, class _Traits1 = std::char_traits<T1>, class _Ax1 = std::allocator<T1>, class T2, class _Traits2 = std::char_traits<T2>, class _Ax2 = std::allocator<T2>>
667 inline int stricmp(
668 _In_ const std::basic_string<T1, _Traits1, _Ax1>& str1,
669 _In_ const std::basic_string<T2, _Traits2, _Ax2>& str2,
670 _In_ const std::locale& locale)
671 {
672 return strnicmp(str1.data(), str1.size(), str2.data(), str2.size(), locale);
673 }
674
684 template <class T1, class T2>
685 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)
686 {
687 _Assume_(str1 || !count);
688 _Assume_(str2 || !count);
689 size_t i; T1 a; T2 b;
690 for (i = 0; i < count && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); i++) {
691 if (a > b) return +1;
692 if (a < b) return -1;
693 }
694 if (i < count && str1[i]) return +1;
695 if (i < count && str2[i]) return -1;
696 return 0;
697 }
698
709 template <class T1, class T2>
710 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)
711 {
712 _Assume_(str1 || !count);
713 _Assume_(str2 || !count);
714 size_t i; T1 a; T2 b;
715 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
716 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
717 for (i = 0; i < count && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
718 if (a > b) return +1;
719 if (a < b) return -1;
720 }
721 if (i < count && str1[i]) return +1;
722 if (i < count && str2[i]) return -1;
723 return 0;
724 }
725
736 template <class T1, class T2>
737 inline int strnicmp(
738 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
739 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
740 {
741 _Assume_(str1 || !count1);
742 _Assume_(str2 || !count2);
743 size_t i; T1 a; T2 b;
744 for (i = 0; i < count1 && i < count2 && ((a = tolower(str1[i])) | (b = tolower(str2[i]))); i++) {
745 if (a > b) return +1;
746 if (a < b) return -1;
747 }
748 if (i < count1 && str1[i]) return +1;
749 if (i < count2 && str2[i]) return -1;
750 return 0;
751 }
752
764 template <class T1, class T2>
765 inline int strnicmp(
766 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
767 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
768 _In_ const std::locale& locale)
769 {
770 _Assume_(str1 || !count1);
771 _Assume_(str2 || !count2);
772 size_t i; T1 a; T2 b;
773 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
774 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
775 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
776 if (a > b) return +1;
777 if (a < b) return -1;
778 }
779 if (i < count1 && str1[i]) return +1;
780 if (i < count2 && str2[i]) return -1;
781 return 0;
782 }
783
792 template <class T1, class T2>
793 inline size_t strstr(
794 _In_z_ const T1* str,
795 _In_z_ const T2* sample)
796 {
797 _Assume_(str);
798 _Assume_(sample);
799 for (size_t offset = 0;; ++offset) {
800 for (size_t i = offset, j = 0;; ++i, ++j) {
801 if (!sample[j])
802 return offset;
803 if (!str[i])
804 return npos;
805 if (str[i] != sample[j])
806 break;
807 }
808 }
809 }
810
820 template <class T1, class T2>
821 inline size_t strnstr(
822 _In_reads_or_z_opt_(count) const T1* str,
823 _In_ size_t count,
824 _In_z_ const T2* sample)
825 {
826 _Assume_(str || !count);
827 _Assume_(sample);
828 for (size_t offset = 0;; ++offset) {
829 for (size_t i = offset, j = 0;; ++i, ++j) {
830 if (!sample[j])
831 return offset;
832 if (i >= count || !str[i])
833 return npos;
834 if (str[i] != sample[j])
835 break;
836 }
837 }
838 }
839
848 template <class T1, class T2>
849 inline size_t stristr(
850 _In_z_ const T1* str,
851 _In_z_ const T2* sample)
852 {
853 _Assume_(str);
854 _Assume_(sample);
855 for (size_t offset = 0;; ++offset) {
856 for (size_t i = offset, j = 0;; ++i, ++j) {
857 if (!sample[j])
858 return offset;
859 if (!str[i])
860 return npos;
861 if (tolower(str[i]) != tolower(sample[j]))
862 break;
863 }
864 }
865 }
866
876 template <class T1, class T2>
877 inline size_t stristr(
878 _In_z_ const T1* str,
879 _In_z_ const T2* sample,
880 _In_ const std::locale& locale)
881 {
882 _Assume_(str);
883 _Assume_(sample);
884 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
885 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
886 for (size_t offset = 0;; ++offset) {
887 for (size_t i = offset, j = 0;; ++i, ++j) {
888 if (!sample[j])
889 return offset;
890 if (!str[i])
891 return npos;
892 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
893 break;
894 }
895 }
896 }
897
907 template <class T1, class T2>
908 inline size_t strnistr(
909 _In_reads_or_z_opt_(count) const T1* str,
910 _In_ size_t count,
911 _In_z_ const T2* sample)
912 {
913 _Assume_(str || !count);
914 _Assume_(sample);
915 for (size_t offset = 0;; ++offset) {
916 for (size_t i = offset, j = 0;; ++i, ++j) {
917 if (!sample[j])
918 return offset;
919 if (i >= count || !str[i])
920 return npos;
921 if (tolower(str[i]) != tolower(sample[j]))
922 break;
923 }
924 }
925 }
926
937 template <class T1, class T2>
938 inline size_t strnistr(
939 _In_reads_or_z_opt_(count) const T1* str,
940 _In_ size_t count,
941 _In_z_ const T2* sample,
942 _In_ const std::locale& locale)
943 {
944 _Assume_(str || !count);
945 _Assume_(sample);
946 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
947 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
948 for (size_t offset = 0;; ++offset) {
949 for (size_t i = offset, j = 0;; ++i, ++j) {
950 if (!sample[j])
951 return offset;
952 if (i >= count || !str[i])
953 return npos;
954 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
955 break;
956 }
957 }
958 }
959
968 template <class T1, class T2>
969 inline size_t strcpy(
970 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
971 _In_z_ const T2* src)
972 {
973 _Assume_(dst && src);
974 for (size_t i = 0; ; ++i) {
975 if ((dst[i] = src[i]) == 0)
976 return i;
977 }
978 }
979
989 template <class T1, class T2>
990 inline size_t strncpy(
991 _Out_writes_(count) _Post_maybez_ T1* dst,
992 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
993 {
994 _Assume_(dst && src || !count);
995 for (size_t i = 0; ; ++i) {
996 if (i >= count)
997 return i;
998 if ((dst[i] = src[i]) == 0)
999 return i;
1000 }
1001 }
1002
1013 template <class T1, class T2>
1014 inline size_t strncpy(
1015 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1016 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1017 {
1018 _Assume_(dst || !count_dst);
1019 _Assume_(src || !count_src);
1020 for (size_t i = 0; ; ++i)
1021 {
1022 if (i >= count_dst)
1023 return i;
1024 if (i >= count_src) {
1025 dst[i] = 0;
1026 return i;
1027 }
1028 if ((dst[i] = src[i]) == 0)
1029 return i;
1030 }
1031 }
1032
1041 template <class T1, class T2>
1042 inline size_t strcat(
1043 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
1044 _In_z_ const T2* src)
1045 {
1046 _Assume_(dst && src);
1047 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1048 if ((dst[j] = src[i]) == 0)
1049 return j;
1050 }
1051 }
1052
1062 template <class T1, class T2>
1063 inline size_t strncat(
1064 _Inout_z_ T1* dst,
1065 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
1066 {
1067 _Assume_(dst && src || !count);
1068 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
1069 if (i >= count)
1070 return j;
1071 if ((dst[j] = src[i]) == 0)
1072 return j;
1073 }
1074 }
1075
1086 template <class T1, class T2>
1087 inline size_t strncat(
1088 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
1089 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
1090 {
1091 _Assume_(dst || !count_dst);
1092 _Assume_(src || !count_src);
1093 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
1094 {
1095 if (j >= count_dst)
1096 return j;
1097 if (i >= count_src) {
1098 dst[j] = 0;
1099 return j;
1100 }
1101 if ((dst[j] = src[i]) == 0)
1102 return j;
1103 }
1104 }
1105
1116 template <class T>
1117 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
1118 {
1119 if (!str) _Unlikely_
1120 return nullptr;
1121 size_t count = strlen(str) + 1;
1122 T* dst = new T[count];
1123 strncpy(dst, count, str, SIZE_MAX);
1124 return dst;
1125 }
1126
1138 template <class T>
1139 inline _Ret_z_ T* strndup(
1140 _In_reads_or_z_opt_(count) const T* str,
1141 _In_ size_t count)
1142 {
1143 T* dst = new T[count];
1144 strncpy(dst, count, str, SIZE_MAX);
1145 return dst;
1146 }
1147
1157 template <class T>
1158 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
1159 {
1160 _Assume_(dst);
1161 _Assume_(src);
1162 size_t i, j;
1163 for (i = j = 0; src[j];) {
1164 if (src[j] != '\r' || src[j + 1] != '\n')
1165 dst[i++] = src[j++];
1166 else {
1167 dst[i++] = '\n';
1168 j += 2;
1169 }
1170 }
1171 dst[i] = 0;
1172 return i;
1173 }
1174
1181 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1182 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& dst, _In_z_ const _Elem* src)
1183 {
1184 _Assume_(src);
1185 _Assume_(src != dst.c_str());
1186 dst.clear();
1187 dst.reserve(strlen(src));
1188 for (size_t j = 0; src[j];) {
1189 if (src[j] != '\r' || src[j + 1] != '\n')
1190 dst += src[j++];
1191 else {
1192 dst += '\n';
1193 j += 2;
1194 }
1195 }
1196 }
1197
1203 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1204 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
1205 {
1206 size_t i, j, n;
1207 for (i = j = 0, n = str.size(); j < n;) {
1208 if (str[j] != '\r' || str[j + 1] != '\n')
1209 str[i++] = str[j++];
1210 else {
1211 str[i++] = '\n';
1212 j += 2;
1213 }
1214 }
1215 str.resize(i);
1216 }
1217
1219 template <class T, class T_bin>
1220 inline T_bin strtoint(
1221 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1222 _Out_opt_ size_t* end,
1223 _In_ int radix,
1224 _Out_ uint8_t& flags)
1225 {
1226 _Assume_(str || !count);
1227 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
1228
1229 size_t i = 0;
1230 T_bin value = 0, digit,
1231 max_ui = (T_bin)-1,
1232 max_ui_pre1, max_ui_pre2;
1233
1234 flags = 0;
1235
1236 // Skip leading spaces.
1237 for (;; ++i) {
1238 if (i >= count || !str[i]) goto error;
1239 if (!isspace(str[i])) break;
1240 }
1241
1242 // Read the sign.
1243 if (str[i] == '+') {
1244 flags &= ~0x01;
1245 ++i;
1246 if (i >= count || !str[i]) goto error;
1247 }
1248 else if (str[i] == '-') {
1249 flags |= 0x01;
1250 ++i;
1251 if (i >= count || !str[i]) goto error;
1252 }
1253
1254 if (radix == 16) {
1255 // On hexadecimal, allow leading 0x.
1256 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
1257 i += 2;
1258 if (i >= count || !str[i]) goto error;
1259 }
1260 }
1261 else if (!radix) {
1262 // Autodetect radix.
1263 if (str[i] == '0') {
1264 ++i;
1265 if (i >= count || !str[i]) goto error;
1266 if (str[i] == 'x' || str[i] == 'X') {
1267 radix = 16;
1268 ++i;
1269 if (i >= count || !str[i]) goto error;
1270 }
1271 else
1272 radix = 8;
1273 }
1274 else
1275 radix = 10;
1276 }
1277
1278 // We have the radix.
1279 max_ui_pre1 = max_ui / (T_bin)radix;
1280 max_ui_pre2 = max_ui % (T_bin)radix;
1281 for (;;) {
1282 if ('0' <= str[i] && str[i] <= '9')
1283 digit = (T_bin)str[i] - '0';
1284 else if ('A' <= str[i] && str[i] <= 'Z')
1285 digit = (T_bin)str[i] - 'A' + '\x0a';
1286 else if ('a' <= str[i] && str[i] <= 'z')
1287 digit = (T_bin)str[i] - 'a' + '\x0a';
1288 else
1289 goto error;
1290 if (digit >= (T_bin)radix)
1291 goto error;
1292
1293 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
1294 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
1295 value = value * (T_bin)radix + digit;
1296 else {
1297 // Overflow!
1298 flags |= 0x02;
1299 }
1300
1301 ++i;
1302 if (i >= count || !str[i])
1303 goto error;
1304 }
1305
1306 error:
1307 if (end) *end = i;
1308 return value;
1309 }
1311
1322 template <class T, class T_bin>
1323 T_bin strtoint(
1324 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1325 _Out_opt_ size_t* end,
1326 _In_ int radix)
1327 {
1328 uint8_t flags;
1329 T_bin value;
1330
1331 switch (sizeof(T_bin)) {
1332 case 1:
1333 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
1334 if ((flags & 0x01) && (value & 0x80)) {
1335 // Sign bit is 1 => overflow.
1336 flags |= 0x02;
1337 }
1338 return (flags & 0x02) ?
1339 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
1340 (flags & 0x01) ? -value : value;
1341
1342 case 2:
1343 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
1344 if ((flags & 0x01) && (value & 0x8000)) {
1345 // Sign bit is 1 => overflow.
1346 flags |= 0x02;
1347 }
1348 return (flags & 0x02) ?
1349 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
1350 (flags & 0x01) ? -value : value;
1351
1352 case 4:
1353 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
1354 if ((flags & 0x01) && (value & 0x80000000)) {
1355 // Sign bit is 1 => overflow.
1356 flags |= 0x02;
1357 }
1358 return (flags & 0x02) ?
1359 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
1360 (flags & 0x01) ? -value : value;
1361
1362 case 8:
1363 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
1364 if ((flags & 0x01) && (value & 0x8000000000000000)) {
1365 // Sign bit is 1 => overflow.
1366 flags |= 0x02;
1367 }
1368 return (flags & 0x02) ?
1369 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
1370 (flags & 0x01) ? -value : value;
1371
1372 default:
1373 throw std::invalid_argument("Unsupported bit length");
1374 }
1375 }
1376
1387 template <class T, class T_bin>
1388 inline T_bin strtouint(
1389 _In_reads_or_z_opt_(count) const T* str,
1390 _In_ size_t count,
1391 _Out_opt_ size_t* end,
1392 _In_ int radix)
1393 {
1394 uint8_t flags;
1395 T_bin value;
1396
1397 switch (sizeof(T_bin)) {
1398 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
1399 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
1400 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
1401 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
1402 default: throw std::invalid_argument("Unsupported bit length");
1403 }
1404
1405 return (flags & 0x02) ?
1406 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
1407 (flags & 0x01) ? ~value : value;
1408 }
1409
1420 template <class T>
1421 inline int32_t strto32(
1422 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1423 _Out_opt_ size_t* end,
1424 _In_ int radix)
1425 {
1426 return strtoint<T, int32_t>(str, count, end, radix);
1427 }
1428
1439 template <class T>
1440 inline int64_t strto64(
1441 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1442 _Out_opt_ size_t* end,
1443 _In_ int radix)
1444 {
1445 return strtoint<T, int64_t>(str, count, end, radix);
1446 }
1447
1459 template <class T>
1460 inline intptr_t strtoi(
1461 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1462 _Out_opt_ size_t* end,
1463 _In_ int radix)
1464 {
1465#if defined(_WIN64) || defined(__LP64__)
1466 return (intptr_t)strto64(str, count, end, radix);
1467#else
1468 return (intptr_t)strto32(str, count, end, radix);
1469#endif
1470 }
1471
1482 template <class T>
1483 inline uint32_t strtou32(
1484 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1485 _Out_opt_ size_t* end,
1486 _In_ int radix)
1487 {
1488 return strtouint<T, uint32_t>(str, count, end, radix);
1489 }
1490
1501 template <class T>
1502 inline uint64_t strtou64(
1503 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1504 _Out_opt_ size_t* end,
1505 _In_ int radix)
1506 {
1507 return strtouint<T, uint64_t>(str, count, end, radix);
1508 }
1509
1521 template <class T>
1522 inline size_t strtoui(
1523 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1524 _Out_opt_ size_t* end,
1525 _In_ int radix)
1526 {
1527#if defined(_WIN64) || defined(__LP64__)
1528 return (size_t)strtou64(str, count, end, radix);
1529#else
1530 return (size_t)strtou32(str, count, end, radix);
1531#endif
1532 }
1533
1535 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)
1536 {
1537 int r;
1538#ifdef _WIN32
1539 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1540#pragma warning(suppress: 4996)
1541 r = _vsnprintf_l(str, capacity, format, locale, arg);
1542#else
1543 r = ::vsnprintf(str, capacity, format, arg);
1544#endif
1545 if (r == -1 && strnlen(str, capacity) == capacity) {
1546 // Buffer overrun. Estimate buffer size for the next iteration.
1547 capacity += std::max<size_t>(capacity / 8, 0x80);
1548 if (capacity > INT_MAX)
1549 throw std::invalid_argument("string too big");
1550 return (int)capacity;
1551 }
1552 return r;
1553 }
1554
1555 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)
1556 {
1557 int r;
1558#ifdef _WIN32
1559 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1560#pragma warning(suppress: 4996)
1561 r = _vsnwprintf_l(str, capacity, format, locale, arg);
1562#else
1563 r = vswprintf(str, capacity, format, arg);
1564#endif
1565 if (r == -1 && strnlen(str, capacity) == capacity) {
1566 // Buffer overrun. Estimate buffer size for the next iteration.
1567 capacity += std::max<size_t>(capacity / 8, 0x80);
1568 if (capacity > INT_MAX)
1569 throw std::invalid_argument("string too big");
1570 return (int)capacity;
1571 }
1572 return r;
1573 }
1575
1586 template<class _Elem, class _Traits, class _Ax>
1587 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)
1588 {
1589 _Elem buf[1024 / sizeof(_Elem)];
1590
1591 // Try with stack buffer first.
1592 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
1593 if (count >= 0) {
1594 // Copy from stack.
1595 str.append(buf, count);
1596 return count;
1597 }
1598 for (size_t capacity = 2 * 1024 / sizeof(_Elem);; capacity *= 2) {
1599 // Allocate on heap and retry.
1600 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1601 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
1602 if (count >= 0) {
1603 str.append(buf_dyn.get(), count);
1604 return count;
1605 }
1606 }
1607 }
1608
1618 template<class _Elem, class _Traits, class _Ax>
1619 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, ...)
1620 {
1621 va_list arg;
1622 va_start(arg, locale);
1623 size_t n = vappendf(str, format, locale, arg);
1624 va_end(arg);
1625 return n;
1626 }
1627
1636 template<class _Elem, class _Traits, class _Ax>
1637 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)
1638 {
1639 str.clear();
1640 vappendf(str, format, locale, arg);
1641 }
1642
1650 template<class _Elem, class _Traits, class _Ax>
1651 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, ...)
1652 {
1653 va_list arg;
1654 va_start(arg, locale);
1655 vsprintf(str, format, locale, arg);
1656 va_end(arg);
1657 }
1658
1668 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1669 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)
1670 {
1671 std::basic_string<_Elem, _Traits, _Ax> str;
1672 vappendf(str, format, locale, arg);
1673 return str;
1674 }
1675
1684 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1685 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem* format, _In_opt_ locale_t locale, ...)
1686 {
1687 va_list arg;
1688 va_start(arg, locale);
1689 auto str = vsprintf(format, locale, arg);
1690 va_end(arg);
1691 return str;
1692 }
1693
1695 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)
1696 {
1697#ifdef _WIN32
1698 return _strftime_l(str, capacity, format, time, locale);
1699#else
1700 return strftime_l(str, capacity, format, time, locale);
1701#endif
1702 }
1703
1704 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)
1705 {
1706#ifdef _WIN32
1707 return _wcsftime_l(str, capacity, format, time, locale);
1708#else
1709 return wcsftime_l(str, capacity, format, time, locale);
1710#endif
1711 }
1713
1722 template<class _Elem, class _Traits, class _Ax>
1723 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)
1724 {
1725 _Elem buf[1024 / sizeof(_Elem)];
1726
1727 // Try with stack buffer first.
1728 size_t count = strftime(buf, _countof(buf), format, time, locale);
1729 if (count) {
1730 // Copy from stack.
1731 str.append(buf, count);
1732 }
1733 else {
1734 for (size_t capacity = 2 * 1024 / sizeof(_Elem);; capacity *= 2) {
1735 // Allocate on heap and retry.
1736 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1737 count = strftime(buf_dyn.get(), capacity, format, time, locale);
1738 if (count) {
1739 str.append(buf_dyn.get(), count);
1740 break;
1741 }
1742 }
1743 }
1744 }
1745
1754 template<class _Elem, class _Traits, class _Ax>
1755 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)
1756 {
1757 str.clear();
1758 strcatftime(str, format, time, locale);
1759 }
1760
1771 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1772 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)
1773 {
1774 std::basic_string<_Elem, _Traits, _Ax> str;
1775 strcatftime(str, format, time, locale);
1776 return str;
1777 }
1778
1784 template<class T>
1785 inline void strlwr(_Inout_z_ T* str)
1786 {
1787 _Assume_(str);
1788 for (size_t i = 0; str[i]; ++i)
1789 str[i] = tolower(str[i]);
1790 }
1791
1798 template<class T>
1799 inline void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
1800 {
1801 _Assume_(str);
1802 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1803 for (size_t i = 0; str[i]; ++i)
1804 str[i] = ctype.tolower(str[i]);
1805 }
1806
1813 template<class T>
1814 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count)
1815 {
1816 _Assume_(str || !count);
1817 for (size_t i = 0; i < count && str[i]; ++i)
1818 str[i] = tolower(str[i]);
1819 }
1820
1826 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1827 inline void strlwr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
1828 {
1829 for (auto& c : str)
1830 c = tolower(c);
1831 }
1832
1840 template<class T>
1841 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1842 {
1843 _Assume_(str || !count);
1844 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1845 for (size_t i = 0; i < count && str[i]; ++i)
1846 str[i] = ctype.tolower(str[i]);
1847 }
1848
1855 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1856 inline void strlwr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str, _In_ const std::locale& locale)
1857 {
1858 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1859 for (auto& c : str)
1860 c = ctype.tolower(c);
1861 }
1862
1868 template<class T>
1869 inline void strupr(_Inout_z_ T* str)
1870 {
1871 _Assume_(str);
1872 for (size_t i = 0; str[i]; ++i)
1873 str[i] = toupper(str[i]);
1874 }
1875
1882 template<class T>
1883 inline void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
1884 {
1885 _Assume_(str);
1886 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1887 for (size_t i = 0; str[i]; ++i)
1888 str[i] = ctype.toupper(str[i]);
1889 }
1890
1897 template<class T>
1898 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count)
1899 {
1900 _Assume_(str || !count);
1901 for (size_t i = 0; i < count && str[i]; ++i)
1902 str[i] = toupper(str[i]);
1903 }
1904
1912 template<class T>
1913 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1914 {
1915 _Assume_(str || !count);
1916 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1917 for (size_t i = 0; i < count && str[i]; ++i)
1918 str[i] = ctype.toupper(str[i]);
1919 }
1920
1926 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1927 inline void strupr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
1928 {
1929 for (auto& c : str)
1930 c = toupper(c);
1931 }
1932
1939 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1940 inline void strupr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str, _In_ const std::locale& locale)
1941 {
1942 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1943 for (auto& c : str)
1944 c = ctype.toupper(c);
1945 }
1946
1955 template<class T>
1956 inline size_t ltrim(
1957 _Inout_z_count_(count) T* str, _In_ size_t count)
1958 {
1959 for (size_t i = 0;; ++i) {
1960 if (i >= count) {
1961 if (count) str[0] = 0;
1962 return 0;
1963 }
1964 if (!str[i]) {
1965 str[0] = 0;
1966 return 0;
1967 }
1968 if (!isspace(str[i])) {
1969 if (!i)
1970 return strnlen(str, count);
1971 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
1972 str[n] = 0;
1973 return n;
1974 }
1975 }
1976 }
1977
1987 template<class T>
1988 inline size_t ltrim(
1989 _Inout_z_count_(count) T* str, _In_ size_t count,
1990 _In_ const std::locale& locale)
1991 {
1992 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1993 for (size_t i = 0;; ++i) {
1994 if (i >= count) {
1995 if (count) str[0] = 0;
1996 return 0;
1997 }
1998 if (!str[i]) {
1999 str[0] = 0;
2000 return 0;
2001 }
2002 if (!ctype.is(ctype.space, str[i])) {
2003 if (!i)
2004 return strnlen(str, count);
2005 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
2006 str[n] = 0;
2007 return n;
2008 }
2009 }
2010 }
2011
2017 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2018 inline void ltrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s)
2019 {
2020 s.erase(
2021 s.begin(),
2022 std::find_if(
2023 s.begin(),
2024 s.end(),
2025 [&](_In_ _Elem ch) { return !isspace(ch); }));
2026 }
2027
2034 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2035 inline void ltrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s, _In_ const std::locale& locale)
2036 {
2037 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
2038 s.erase(
2039 s.begin(),
2040 std::find_if(
2041 s.begin(),
2042 s.end(),
2043 [&](_In_ _Elem ch) { return !ctype.is(ctype.space, ch); }));
2044 }
2045
2054 template<class T>
2055 inline size_t rtrim(
2056 _Inout_z_count_(count) T* str, _In_ size_t count)
2057 {
2058 for (size_t i = 0, j = 0;;) {
2059 if (i >= count || !str[i]) {
2060 if (j < count) str[j] = 0;
2061 return j;
2062 }
2063 if (!isspace(str[i]))
2064 j = ++i;
2065 else
2066 ++i;
2067 }
2068 }
2069
2079 template<class T>
2080 inline size_t rtrim(
2081 _Inout_z_count_(count) T* str, _In_ size_t count,
2082 _In_ const std::locale& locale)
2083 {
2084 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
2085 for (size_t i = 0, j = 0;;) {
2086 if (i >= count || !str[i]) {
2087 if (j < count) str[j] = 0;
2088 return j;
2089 }
2090 if (!ctype.is(ctype.space, str[i]))
2091 j = ++i;
2092 else
2093 ++i;
2094 }
2095 }
2096
2102 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2103 static inline void rtrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s)
2104 {
2105 s.erase(
2106 std::find_if(
2107 s.rbegin(),
2108 s.rend(),
2109 [&](_In_ _Elem ch) { return !isspace(ch); }).base(),
2110 s.end());
2111 }
2112
2119 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2120 static inline void rtrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s, _In_ const std::locale& locale)
2121 {
2122 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
2123 s.erase(
2124 std::find_if(
2125 s.rbegin(),
2126 s.rend(),
2127 [&](_In_ _Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
2128 s.end());
2129 }
2130
2139 template<class T>
2140 inline size_t trim(
2141 _Inout_z_count_(count) T* str, _In_ size_t count)
2142 {
2143 return ltrim(str, rtrim(str, count));
2144 }
2145
2155 template<class T>
2156 inline size_t trim(
2157 _Inout_z_count_(count) T* str, _In_ size_t count,
2158 _In_ const std::locale& locale)
2159 {
2160 return ltrim(str, rtrim(str, count, locale), locale);
2161 }
2162
2168 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2169 static inline void trim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s)
2170 {
2171 auto nonspace = [&](_In_ _Elem ch) { return !isspace(ch); };
2172 s.erase(
2173 s.begin(),
2174 std::find_if(
2175 s.begin(),
2176 s.end(),
2177 nonspace));
2178 s.erase(
2179 std::find_if(
2180 s.rbegin(),
2181 s.rend(),
2182 nonspace).base(),
2183 s.end());
2184 }
2185
2192 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
2193 static inline void trim(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& s, _In_ const std::locale& locale)
2194 {
2195 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
2196 auto nonspace = [&](_In_ _Elem ch) { return !ctype.is(ctype.space, ch); };
2197 s.erase(
2198 s.begin(),
2199 std::find_if(
2200 s.begin(),
2201 s.end(),
2202 nonspace));
2203 s.erase(
2204 std::find_if(
2205 s.rbegin(),
2206 s.rend(),
2207 nonspace).base(),
2208 s.end());
2209 }
2210}