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 <ctype.h>
10#include <locale.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 <memory>
23#include <stdexcept>
24
25namespace stdex
26{
27#ifdef _WIN32
28 using locale_t = _locale_t;
29
30 inline locale_t create_locale(_In_ int category, _In_z_ const char* locale) { return _create_locale(category, locale); }
31 inline locale_t create_locale(_In_ int category, _In_z_ const wchar_t* locale) { return _wcreate_locale(category, locale); }
32 inline void free_locale(_In_opt_ locale_t locale) { _free_locale(locale); }
33#else
34 using locale_t = ::locale_t;
35
36 inline locale_t create_locale(_In_ int category, _In_z_ const char* locale)
37 {
38 int mask = 0;
39 switch (category) {
40 case LC_ALL : mask = LC_ALL_MASK ; break;
41 case LC_COLLATE : mask = LC_COLLATE_MASK ; break;
42 case LC_CTYPE : mask = LC_CTYPE_MASK ; break;
43 case LC_MESSAGES: mask = LC_MESSAGES_MASK; break;
44 case LC_MONETARY: mask = LC_MONETARY_MASK; break;
45 case LC_NUMERIC : mask = LC_NUMERIC_MASK ; break;
46 case LC_TIME : mask = LC_TIME_MASK ; break;
47 }
48 return newlocale(mask, locale, LC_GLOBAL_LOCALE);
49 }
50
51 inline void free_locale(_In_opt_ locale_t locale) { freelocale(locale); }
52#endif
53
58 {
62 void operator()(_In_ locale_t locale) const
63 {
64 free_locale(locale);
65 }
66 };
67
71#if defined(_WIN32)
72 using locale = std::unique_ptr<__crt_locale_pointers, free_locale_delete>;
73#elif defined(__APPLE__)
74 using locale = std::unique_ptr<struct _xlocale, free_locale_delete>;
75#else
76 using locale = std::unique_ptr<struct __locale_struct, free_locale_delete>;
77#endif
78
82 const locale locale_C(create_locale(LC_ALL, "C"));
83
87#ifdef _WIN32
88 typedef wchar_t utf16_t;
89#else
90 typedef char16_t utf16_t;
91#endif
92
98 inline bool is_high_surrogate(_In_ utf16_t chr)
99 {
100 return 0xd800 < chr && chr < 0xdc00;
101 }
102
108 inline bool is_low_surrogate(_In_ utf16_t chr)
109 {
110 return 0xdc00 < chr && chr < 0xe000;
111 }
112
118 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
119 {
120 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
121 }
122
128 inline char32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
129 {
130 _Assume_(is_surrogate_pair(str));
131 return
132 ((char32_t)(str[0] - 0xd800) << 10) +
133 (char32_t)(str[1] - 0xdc00) +
134 0x10000;
135 }
136
142 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ char32_t chr)
143 {
144 _Assume_(chr >= 0x10000);
145 chr -= 0x10000;
146 str[0] = 0xd800 + (char32_t)((chr >> 10) & 0x3ff);
147 str[1] = 0xdc00 + (char32_t)(chr & 0x3ff);
148 }
149
155 inline bool iscombining(_In_ char32_t chr)
156 {
157 return
158 (0x0300 <= chr && chr < 0x0370) ||
159 (0x1dc0 <= chr && chr < 0x1e00) ||
160 (0x20d0 <= chr && chr < 0x2100) ||
161 (0xfe20 <= chr && chr < 0xfe30);
162 }
163
169 template <class T>
170 inline size_t islbreak(_In_ T chr)
171 {
172 return chr == '\n' || chr == '\r';
173 }
174
181 template <class T>
182 inline size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
183 {
184 _Assume_(chr || !count);
185 if (count >= 2 && ((chr[0] == '\r' && chr[1] == '\n') || (chr[0] == '\n' && chr[1] == '\r')))
186 return 2;
187 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
188 return 1;
189 return 0;
190 }
191
198 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
199 {
200 _Assume_(glyph || !count);
201 if (count) {
202#ifdef _WIN32
203 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
204#else
205 size_t i = 1;
206#endif
207 for (; i < count && iscombining(glyph[i]); ++i);
208 return i;
209 }
210 return 0;
211 }
212
220 template <class T>
221 inline size_t strlen(_In_z_ const T* str)
222 {
223 _Assume_(str);
224 size_t i;
225 for (i = 0; str[i]; ++i);
226 return i;
227 }
228
237 template <class T>
238 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
239 {
240 _Assume_(str || !count);
241 size_t i;
242 for (i = 0; i < count && str[i]; ++i);
243 return i;
244 }
245
246 constexpr auto npos{ static_cast<size_t>(-1) };
247
256 template <class T>
257 inline size_t strchr(_In_z_ const T* str, _In_ T chr)
258 {
259 _Assume_(str);
260 for (size_t i = 0; str[i]; ++i)
261 if (str[i] == chr) return i;
262 return npos;
263 }
264
274 template <class T>
275 inline size_t strnchr(
276 _In_reads_or_z_opt_(count) const T* str,
277 _In_ size_t count,
278 _In_ T chr)
279 {
280 _Assume_(str || !count);
281 for (size_t i = 0; i < count && str[i]; ++i)
282 if (str[i] == chr) return i;
283 return npos;
284 }
285
295 template <class T>
296 inline size_t strrnchr(
297 _In_reads_or_z_opt_(count) const T* str,
298 _In_ size_t count,
299 _In_ T chr)
300 {
301 _Assume_(str || !count);
302 size_t z = npos;
303 for (size_t i = 0; i < count && str[i]; ++i)
304 if (str[i] == chr) z = i;
305 return z;
306 }
307
317 template <class T>
318 inline size_t strnichr(
319 _In_reads_or_z_opt_(count) const T* str,
320 _In_ size_t count,
321 _In_ T chr,
322 _In_ const std::locale& locale)
323 {
324 _Assume_(str || !count);
325 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
326 chr = ctype.tolower(chr);
327 for (size_t i = 0; i < count && str[i]; ++i)
328 if (ctype.tolower(str[i]) == chr) return i;
329 return npos;
330 }
331
341 template <class T>
342 inline size_t strrnichr(
343 _In_reads_or_z_opt_(count) const T* str,
344 _In_ size_t count,
345 _In_ T chr,
346 _In_ const std::locale& locale)
347 {
348 _Assume_(str || !count);
349 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
350 chr = ctype.tolower(chr);
351 size_t z = npos;
352 for (size_t i = 0; i < count && str[i]; ++i)
353 if (ctype.tolower(str[i]) == chr) z = i;
354 return z;
355 }
356
365 template <class T1, class T2>
366 inline int strcmp(const T1* str1, const T2* str2)
367 {
368 _Assume_(str1 && str2);
369 T1 a; T2 b;
370 for (size_t i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
371 if (a > b) return +1;
372 if (a < b) return -1;
373 }
374 return 0;
375 }
376
387 template <class T1, class T2>
388 inline int strncmp(
389 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
390 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
391 {
392 _Assume_(str1 || !count1);
393 _Assume_(str2 || !count2);
394 size_t i; T1 a; T2 b;
395 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
396 if (a > b) return +1;
397 if (a < b) return -1;
398 }
399 if (i < count1 && str1[i]) return +1;
400 if (i < count2 && str2[i]) return -1;
401 return 0;
402 }
403
413 template <class T1, class T2>
414 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)
415 {
416 _Assume_((str1 && str2) || !count);
417 size_t i; T1 a; T2 b;
418 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
419 if (a > b) return +1;
420 if (a < b) return -1;
421 }
422 if (i < count && str1[i]) return +1;
423 if (i < count && str2[i]) return -1;
424 return 0;
425 }
426
437 template <class T>
438 inline int strncoll(
439 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
440 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
441 _In_ const std::locale& locale)
442 {
443 _Assume_(str1 || !count1);
444 _Assume_(str2 || !count2);
445 auto& collate = std::use_facet<std::collate<T>>(locale);
446 return collate.compare(str1, str1 + count1, str2, str2 + count2);
447 }
448
457 template <class T1, class T2>
458 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
459 {
460 _Assume_(str1);
461 _Assume_(str2);
462 size_t i; T1 a; T2 b;
463 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
464 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
465 for (i = 0; (a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i])); i++) {
466 if (a > b) return +1;
467 if (a < b) return -1;
468 }
469 if (str1[i]) return +1;
470 if (str2[i]) return -1;
471 return 0;
472 }
473
483 template <class T1, class T2>
484 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)
485 {
486 _Assume_(str1 || !count);
487 _Assume_(str2 || !count);
488 size_t i; T1 a; T2 b;
489 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
490 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
491 for (i = 0; i < count && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
492 if (a > b) return +1;
493 if (a < b) return -1;
494 }
495 if (i < count && str1[i]) return +1;
496 if (i < count && str2[i]) return -1;
497 return 0;
498 }
499
510 template <class T1, class T2>
511 inline int strnicmp(
512 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
513 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
514 _In_ const std::locale& locale)
515 {
516 _Assume_(str1 || !count1);
517 _Assume_(str2 || !count2);
518 size_t i; T1 a; T2 b;
519 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
520 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
521 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
522 if (a > b) return +1;
523 if (a < b) return -1;
524 }
525 if (i < count1 && str1[i]) return +1;
526 if (i < count2 && str2[i]) return -1;
527 return 0;
528 }
529
538 template <class T1, class T2>
539 inline size_t strstr(
540 _In_z_ const T1* str,
541 _In_z_ const T2* sample)
542 {
543 _Assume_(str);
544 _Assume_(sample);
545 for (size_t offset = 0;; ++offset) {
546 for (size_t i = offset, j = 0;; ++i, ++j) {
547 if (!sample[j])
548 return offset;
549 if (!str[i])
550 return npos;
551 if (str[i] != sample[j])
552 break;
553 }
554 }
555 }
556
566 template <class T1, class T2>
567 inline size_t strnstr(
568 _In_reads_or_z_opt_(count) const T1* str,
569 _In_ size_t count,
570 _In_z_ const T2* sample)
571 {
572 _Assume_(str || !count);
573 _Assume_(sample);
574 for (size_t offset = 0;; ++offset) {
575 for (size_t i = offset, j = 0;; ++i, ++j) {
576 if (!sample[j])
577 return offset;
578 if (i >= count || !str[i])
579 return npos;
580 if (str[i] != sample[j])
581 break;
582 }
583 }
584 }
585
594 template <class T1, class T2>
595 inline size_t stristr(
596 _In_z_ const T1* str,
597 _In_z_ const T2* sample,
598 _In_ const std::locale& locale)
599 {
600 _Assume_(str);
601 _Assume_(sample);
602 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
603 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
604 for (size_t offset = 0;; ++offset) {
605 for (size_t i = offset, j = 0;; ++i, ++j) {
606 if (!sample[j])
607 return offset;
608 if (!str[i])
609 return npos;
610 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
611 break;
612 }
613 }
614 }
615
625 template <class T1, class T2>
626 inline size_t strnistr(
627 _In_reads_or_z_opt_(count) const T1* str,
628 _In_ size_t count,
629 _In_z_ const T2* sample,
630 _In_ const std::locale& locale)
631 {
632 _Assume_(str || !count);
633 _Assume_(sample);
634 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
635 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
636 for (size_t offset = 0;; ++offset) {
637 for (size_t i = offset, j = 0;; ++i, ++j) {
638 if (!sample[j])
639 return offset;
640 if (i >= count || !str[i])
641 return npos;
642 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
643 break;
644 }
645 }
646 }
647
656 template <class T1, class T2>
657 inline size_t strcpy(
658 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
659 _In_z_ const T2* src)
660 {
661 _Assume_(dst && src);
662 for (size_t i = 0; ; ++i) {
663 if ((dst[i] = src[i]) == 0)
664 return i;
665 }
666 }
667
677 template <class T1, class T2>
678 inline size_t strncpy(
679 _Out_writes_(count) _Post_maybez_ T1* dst,
680 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
681 {
682 _Assume_(dst && src || !count);
683 for (size_t i = 0; ; ++i) {
684 if (i >= count)
685 return i;
686 if ((dst[i] = src[i]) == 0)
687 return i;
688 }
689 }
690
701 template <class T1, class T2>
702 inline size_t strncpy(
703 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
704 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
705 {
706 _Assume_(dst || !count_dst);
707 _Assume_(src || !count_src);
708 for (size_t i = 0; ; ++i)
709 {
710 if (i >= count_dst)
711 return i;
712 if (i >= count_src) {
713 dst[i] = 0;
714 return i;
715 }
716 if ((dst[i] = src[i]) == 0)
717 return i;
718 }
719 }
720
729 template <class T1, class T2>
730 inline size_t strcat(
731 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
732 _In_z_ const T2* src)
733 {
734 _Assume_(dst && src);
735 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
736 if ((dst[j] = src[i]) == 0)
737 return j;
738 }
739 }
740
750 template <class T1, class T2>
751 inline size_t strncat(
752 _Inout_z_ T1* dst,
753 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
754 {
755 _Assume_(dst && src || !count);
756 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
757 if (i >= count)
758 return j;
759 if ((dst[j] = src[i]) == 0)
760 return j;
761 }
762 }
763
774 template <class T1, class T2>
775 inline size_t strncat(
776 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
777 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
778 {
779 _Assume_(dst || !count_dst);
780 _Assume_(src || !count_src);
781 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
782 {
783 if (j >= count_dst)
784 return j;
785 if (i >= count_src) {
786 dst[j] = 0;
787 return j;
788 }
789 if ((dst[j] = src[i]) == 0)
790 return j;
791 }
792 }
793
804 template <class T>
805 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
806 {
807 if (!str) _Unlikely_
808 return nullptr;
809 size_t count = strlen(str) + 1;
810 T* dst = new T[count];
811 strncpy(dst, count, str, SIZE_MAX);
812 return dst;
813 }
814
826 template <class T>
827 inline _Ret_z_ T* strndup(
828 _In_reads_or_z_opt_(count) const T* str,
829 _In_ size_t count)
830 {
831 T* dst = new T[count];
832 strncpy(dst, count, str, SIZE_MAX);
833 return dst;
834 }
835
845 template <class T>
846 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
847 {
848 _Assume_(dst);
849 _Assume_(src);
850 size_t i, j;
851 for (i = j = 0; src[j];) {
852 if (src[j] != '\r' || src[j + 1] != '\n')
853 dst[i++] = src[j++];
854 else {
855 dst[i++] = '\n';
856 j += 2;
857 }
858 }
859 dst[i] = 0;
860 return i;
861 }
862
869 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
870 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &dst, _In_z_ const _Elem* src)
871 {
872 _Assume_(src);
873 _Assume_(src != dst.c_str());
874 dst.clear();
875 dst.reserve(strlen(src));
876 for (size_t j = 0; src[j];) {
877 if (src[j] != '\r' || src[j + 1] != '\n')
878 dst += src[j++];
879 else {
880 dst += '\n';
881 j += 2;
882 }
883 }
884 }
885
891 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
892 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
893 {
894 size_t i, j, n;
895 for (i = j = 0, n = str.size(); j < n;) {
896 if (str[j] != '\r' || str[j + 1] != '\n')
897 str[i++] = str[j++];
898 else {
899 str[i++] = '\n';
900 j += 2;
901 }
902 }
903 str.resize(i);
904 }
905
907 template <class T, class T_bin>
908 inline T_bin strtoint(
909 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
910 _Out_opt_ size_t* end,
911 _In_ int radix,
912 _Out_ uint8_t& flags)
913 {
914 _Assume_(str || !count);
915 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
916
917 size_t i = 0;
918 T_bin value = 0, digit,
919 max_ui = (T_bin)-1,
920 max_ui_pre1, max_ui_pre2;
921
922 flags = 0;
923
924 // Skip leading spaces.
925 for (;; ++i) {
926 if (i >= count || !str[i]) goto error;
927 if (!isspace(str[i])) break;
928 }
929
930 // Read the sign.
931 if (str[i] == '+') {
932 flags &= ~0x01;
933 ++i;
934 if (i >= count || !str[i]) goto error;
935 }
936 else if (str[i] == '-') {
937 flags |= 0x01;
938 ++i;
939 if (i >= count || !str[i]) goto error;
940 }
941
942 if (radix == 16) {
943 // On hexadecimal, allow leading 0x.
944 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
945 i += 2;
946 if (i >= count || !str[i]) goto error;
947 }
948 }
949 else if (!radix) {
950 // Autodetect radix.
951 if (str[i] == '0') {
952 ++i;
953 if (i >= count || !str[i]) goto error;
954 if (str[i] == 'x' || str[i] == 'X') {
955 radix = 16;
956 ++i;
957 if (i >= count || !str[i]) goto error;
958 }
959 else
960 radix = 8;
961 }
962 else
963 radix = 10;
964 }
965
966 // We have the radix.
967 max_ui_pre1 = max_ui / (T_bin)radix;
968 max_ui_pre2 = max_ui % (T_bin)radix;
969 for (;;) {
970 if ('0' <= str[i] && str[i] <= '9')
971 digit = (T_bin)str[i] - '0';
972 else if ('A' <= str[i] && str[i] <= 'Z')
973 digit = (T_bin)str[i] - 'A' + '\x0a';
974 else if ('a' <= str[i] && str[i] <= 'z')
975 digit = (T_bin)str[i] - 'a' + '\x0a';
976 else
977 goto error;
978 if (digit >= (T_bin)radix)
979 goto error;
980
981 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
982 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
983 value = value * (T_bin)radix + digit;
984 else {
985 // Overflow!
986 flags |= 0x02;
987 }
988
989 ++i;
990 if (i >= count || !str[i])
991 goto error;
992 }
993
994 error:
995 if (end) *end = i;
996 return value;
997 }
999
1010 template <class T, class T_bin>
1011 T_bin strtoint(
1012 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1013 _Out_opt_ size_t* end,
1014 _In_ int radix)
1015 {
1016 uint8_t flags;
1017 T_bin value;
1018
1019 switch (sizeof(T_bin)) {
1020 case 1:
1021 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
1022 if ((flags & 0x01) && (value & 0x80)) {
1023 // Sign bit is 1 => overflow.
1024 flags |= 0x02;
1025 }
1026 return (flags & 0x02) ?
1027 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
1028 (flags & 0x01) ? -value : value;
1029
1030 case 2:
1031 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
1032 if ((flags & 0x01) && (value & 0x8000)) {
1033 // Sign bit is 1 => overflow.
1034 flags |= 0x02;
1035 }
1036 return (flags & 0x02) ?
1037 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
1038 (flags & 0x01) ? -value : value;
1039
1040 case 4:
1041 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
1042 if ((flags & 0x01) && (value & 0x80000000)) {
1043 // Sign bit is 1 => overflow.
1044 flags |= 0x02;
1045 }
1046 return (flags & 0x02) ?
1047 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
1048 (flags & 0x01) ? -value : value;
1049
1050 case 8:
1051 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
1052 if ((flags & 0x01) && (value & 0x8000000000000000)) {
1053 // Sign bit is 1 => overflow.
1054 flags |= 0x02;
1055 }
1056 return (flags & 0x02) ?
1057 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
1058 (flags & 0x01) ? -value : value;
1059
1060 default:
1061 throw std::invalid_argument("Unsupported bit length");
1062 }
1063 }
1064
1075 template <class T, class T_bin>
1076 inline T_bin strtouint(
1077 _In_reads_or_z_opt_(count) const T* str,
1078 _In_ size_t count,
1079 _Out_opt_ size_t* end,
1080 _In_ int radix)
1081 {
1082 uint8_t flags;
1083 T_bin value;
1084
1085 switch (sizeof(T_bin)) {
1086 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
1087 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
1088 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
1089 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
1090 default: throw std::invalid_argument("Unsupported bit length");
1091 }
1092
1093 return (flags & 0x02) ?
1094 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
1095 (flags & 0x01) ? ~value : value;
1096 }
1097
1108 template <class T>
1109 inline int32_t strto32(
1110 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1111 _Out_opt_ size_t* end,
1112 _In_ int radix)
1113 {
1114 return strtoint<T, int32_t>(str, count, end, radix);
1115 }
1116
1127 template <class T>
1128 inline int64_t strto64(
1129 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1130 _Out_opt_ size_t* end,
1131 _In_ int radix)
1132 {
1133 return strtoint<T, int64_t>(str, count, end, radix);
1134 }
1135
1147 template <class T>
1148 inline intptr_t strtoi(
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 {
1153#if defined(_WIN64) || defined(__LP64__)
1154 return (intptr_t)strto64(str, count, end, radix);
1155#else
1156 return (intptr_t)strto32(str, count, end, radix);
1157#endif
1158 }
1159
1170 template <class T>
1171 inline uint32_t strtou32(
1172 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1173 _Out_opt_ size_t* end,
1174 _In_ int radix)
1175 {
1176 return strtouint<T, uint32_t>(str, count, end, radix);
1177 }
1178
1189 template <class T>
1190 inline uint64_t strtou64(
1191 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1192 _Out_opt_ size_t* end,
1193 _In_ int radix)
1194 {
1195 return strtouint<T, uint64_t>(str, count, end, radix);
1196 }
1197
1209 template <class T>
1210 inline size_t strtoui(
1211 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1212 _Out_opt_ size_t* end,
1213 _In_ int radix)
1214 {
1215#if defined(_WIN64) || defined(__LP64__)
1216 return (size_t)strtou64(str, count, end, radix);
1217#else
1218 return (size_t)strtou32(str, count, end, radix);
1219#endif
1220 }
1221
1223 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)
1224 {
1225 int r;
1226#ifdef _WIN32
1227 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1228#pragma warning(suppress: 4996)
1229 r = _vsnprintf_l(str, capacity, format, locale, arg);
1230#else
1231 r = ::vsnprintf(str, capacity, format, arg);
1232#endif
1233 if (r == -1 && strnlen(str, capacity) == capacity) {
1234 // Buffer overrun. Estimate buffer size for the next iteration.
1235 capacity += std::max<size_t>(capacity / 8, 0x80);
1236 if (capacity > INT_MAX)
1237 throw std::invalid_argument("string too big");
1238 return (int)capacity;
1239 }
1240 return r;
1241 }
1242
1243 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)
1244 {
1245 int r;
1246#ifdef _WIN32
1247 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1248#pragma warning(suppress: 4996)
1249 r = _vsnwprintf_l(str, capacity, format, locale, arg);
1250#else
1251 r = vswprintf(str, capacity, format, arg);
1252#endif
1253 if (r == -1 && strnlen(str, capacity) == capacity) {
1254 // Buffer overrun. Estimate buffer size for the next iteration.
1255 capacity += std::max<size_t>(capacity / 8, 0x80);
1256 if (capacity > INT_MAX)
1257 throw std::invalid_argument("string too big");
1258 return (int)capacity;
1259 }
1260 return r;
1261 }
1263
1272 template<class _Elem, class _Traits, class _Ax>
1273 inline void vappendf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, _In_ va_list arg)
1274 {
1275 _Elem buf[1024/sizeof(_Elem)];
1276
1277 // Try with stack buffer first.
1278 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
1279 if (count >= 0) {
1280 // Copy from stack.
1281 str.append(buf, count);
1282 } else {
1283 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1284 // Allocate on heap and retry.
1285 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1286 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
1287 if (count >= 0) {
1288 str.append(buf_dyn.get(), count);
1289 break;
1290 }
1291 }
1292 }
1293 }
1294
1302 template<class _Elem, class _Traits, class _Ax>
1303 inline void appendf(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &str, _In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1304 {
1305 va_list arg;
1306 va_start(arg, locale);
1307 vappendf(str, format, locale, arg);
1308 va_end(arg);
1309 }
1310
1319 template<class _Elem, class _Traits, class _Ax>
1320 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)
1321 {
1322 str.clear();
1323 vappendf(str, format, locale, arg);
1324 }
1325
1333 template<class _Elem, class _Traits, class _Ax>
1334 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, ...)
1335 {
1336 va_list arg;
1337 va_start(arg, locale);
1338 vsprintf(str, format, locale, arg);
1339 va_end(arg);
1340 }
1341
1351 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1352 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)
1353 {
1354 std::basic_string<_Elem, _Traits, _Ax> str;
1355 vappendf(str, format, locale, arg);
1356 return str;
1357 }
1358
1367 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1368 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1369 {
1370 va_list arg;
1371 va_start(arg, locale);
1372 auto str = vsprintf(format, locale, arg);
1373 va_end(arg);
1374 return str;
1375 }
1376
1378 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)
1379 {
1380#ifdef _WIN32
1381 return _strftime_l(str, capacity, format, time, locale);
1382#else
1383 return strftime_l(str, capacity, format, time, locale);
1384#endif
1385 }
1386
1387 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)
1388 {
1389#ifdef _WIN32
1390 return _wcsftime_l(str, capacity, format, time, locale);
1391#else
1392 return wcsftime_l(str, capacity, format, time, locale);
1393#endif
1394 }
1396
1405 template<class _Elem, class _Traits, class _Ax>
1406 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)
1407 {
1408 _Elem buf[1024/sizeof(_Elem)];
1409
1410 // Try with stack buffer first.
1411 size_t count = strftime(buf, _countof(buf), format, time, locale);
1412 if (count) {
1413 // Copy from stack.
1414 str.append(buf, count);
1415 } else {
1416 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1417 // Allocate on heap and retry.
1418 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1419 count = strftime(buf_dyn.get(), capacity, format, time, locale);
1420 if (count) {
1421 str.append(buf_dyn.get(), count);
1422 break;
1423 }
1424 }
1425 }
1426 }
1427
1436 template<class _Elem, class _Traits, class _Ax>
1437 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)
1438 {
1439 str.clear();
1440 strcatftime(str, format, time, locale);
1441 }
1442
1453 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1454 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)
1455 {
1456 std::basic_string<_Elem, _Traits, _Ax> str;
1457 strcatftime(str, format, time, locale);
1458 return str;
1459 }
1460
1468 template<class T>
1469 inline void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
1470 {
1471 _Assume_(str);
1472 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1473 for (size_t i = 0; str[i]; ++i)
1474 str[i] = ctype.tolower(str[i]);
1475 }
1476
1485 template<class T>
1486 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1487 {
1488 _Assume_(str || !count);
1489 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1490 for (size_t i = 0; i < count && str[i]; ++i)
1491 str[i] = ctype.tolower(str[i]);
1492 }
1493
1501 template<class T>
1502 inline void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
1503 {
1504 _Assume_(str);
1505 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1506 for (size_t i = 0; str[i]; ++i)
1507 str[i] = ctype.toupper(str[i]);
1508 }
1509
1518 template<class T>
1519 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1520 {
1521 _Assume_(str || !count);
1522 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1523 for (size_t i = 0; i < count && str[i]; ++i)
1524 str[i] = ctype.toupper(str[i]);
1525 }
1526
1534 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1535 inline void strupr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str, _In_ const std::locale& locale)
1536 {
1537 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1538 for (size_t i = 0; i < str.size(); ++i)
1539 str[i] = ctype.toupper(str[i]);
1540 }
1541
1550 template<class T>
1551 inline size_t ltrim(
1552 _Inout_z_count_(count) T* str, _In_ size_t count,
1553 _In_ const std::locale& locale)
1554 {
1555 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1556 for (size_t i = 0;; ++i) {
1557 if (i >= count) {
1558 if (count) str[0] = 0;
1559 return 0;
1560 }
1561 if (!str[i]) {
1562 str[0] = 0;
1563 return 0;
1564 }
1565 if (!ctype.is(ctype.space, str[i])) {
1566 if (!i)
1567 return strnlen(str, count);
1568 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
1569 str[n] = 0;
1570 return n;
1571 }
1572 }
1573 }
1574
1580 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1581 inline void ltrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1582 {
1583 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1584 s.erase(
1585 s.begin(),
1586 std::find_if(
1587 s.begin(),
1588 s.end(),
1589 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }));
1590 }
1591
1600 template<class T>
1601 inline size_t rtrim(
1602 _Inout_z_count_(count) T* str, _In_ size_t count,
1603 _In_ const std::locale& locale)
1604 {
1605 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1606 for (size_t i = 0, j = 0;;) {
1607 if (i >= count || !str[i]) {
1608 if (j < count) str[j] = 0;
1609 return j;
1610 }
1611 if (!ctype.is(ctype.space, str[i]))
1612 j = ++i;
1613 else
1614 ++i;
1615 }
1616 }
1617
1623 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1624 static inline void rtrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1625 {
1626 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1627 s.erase(
1628 std::find_if(
1629 s.rbegin(),
1630 s.rend(),
1631 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
1632 s.end());
1633 }
1634
1643 template<class T>
1644 inline size_t trim(
1645 _Inout_z_count_(count) T* str, _In_ size_t count,
1646 _In_ const std::locale& locale)
1647 {
1648 return ltrim(str, rtrim(str, count, locale), locale);
1649 }
1650
1656 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1657 static inline void trim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1658 {
1659 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1660 s.erase(
1661 s.begin(),
1662 std::find_if(
1663 s.begin(),
1664 s.end(),
1665 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }));
1666 s.erase(
1667 std::find_if(
1668 s.rbegin(),
1669 s.rend(),
1670 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
1671 s.end());
1672 }
1673}
Deleter for unique_ptr using free_locale.
Definition string.hpp:58
void operator()(locale_t locale) const
Delete a pointer.
Definition string.hpp:62