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