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#ifdef __APPLE__
17#include <xlocale.h>
18#endif
19#ifdef _WIN32
20#include <rpcdce.h>
21#else
22#include <uuid/uuid.h>
23#endif
24#include <algorithm>
25#include <locale>
26#include <memory>
27#include <stdexcept>
28
29namespace stdex
30{
31#ifdef _WIN32
32 using locale_t = _locale_t;
33
34 inline locale_t create_locale(_In_ int category, _In_z_ const char* locale) { return _create_locale(category, locale); }
35 inline locale_t create_locale(_In_ int category, _In_z_ const wchar_t* locale) { return _wcreate_locale(category, locale); }
36 inline void free_locale(_In_opt_ locale_t locale) { _free_locale(locale); }
37#else
38 using locale_t = ::locale_t;
39
40 inline locale_t create_locale(_In_ int category, _In_z_ const char* locale)
41 {
42 int mask = 0;
43 switch (category) {
44 case LC_ALL : mask = LC_ALL_MASK ; break;
45 case LC_COLLATE : mask = LC_COLLATE_MASK ; break;
46 case LC_CTYPE : mask = LC_CTYPE_MASK ; break;
47 case LC_MESSAGES: mask = LC_MESSAGES_MASK; break;
48 case LC_MONETARY: mask = LC_MONETARY_MASK; break;
49 case LC_NUMERIC : mask = LC_NUMERIC_MASK ; break;
50 case LC_TIME : mask = LC_TIME_MASK ; break;
51 }
52 return newlocale(mask, locale, LC_GLOBAL_LOCALE);
53 }
54
55 inline void free_locale(_In_opt_ locale_t locale) { freelocale(locale); }
56#endif
57
62 {
66 void operator()(_In_ locale_t locale) const
67 {
68 free_locale(locale);
69 }
70 };
71
75#if defined(_WIN32)
76 using locale = std::unique_ptr<__crt_locale_pointers, free_locale_delete>;
77#elif defined(__APPLE__)
78 using locale = std::unique_ptr<struct _xlocale, free_locale_delete>;
79#else
80 using locale = std::unique_ptr<struct __locale_struct, free_locale_delete>;
81#endif
82
86 const locale locale_C(create_locale(LC_ALL, "C"));
87
91#ifdef _WIN32
92 typedef wchar_t utf16_t;
93#else
94 typedef char16_t utf16_t;
95#endif
96
102 inline bool is_high_surrogate(_In_ utf16_t chr)
103 {
104 return 0xd800 < chr && chr < 0xdc00;
105 }
106
112 inline bool is_low_surrogate(_In_ utf16_t chr)
113 {
114 return 0xdc00 < chr && chr < 0xe000;
115 }
116
122 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
123 {
124 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
125 }
126
132 inline char32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
133 {
134 _Assume_(is_surrogate_pair(str));
135 return
136 ((char32_t)(str[0] - 0xd800) << 10) +
137 (char32_t)(str[1] - 0xdc00) +
138 0x10000;
139 }
140
146 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ char32_t chr)
147 {
148 _Assume_(chr >= 0x10000);
149 chr -= 0x10000;
150 str[0] = 0xd800 + (char32_t)((chr >> 10) & 0x3ff);
151 str[1] = 0xdc00 + (char32_t)(chr & 0x3ff);
152 }
153
159 inline bool iscombining(_In_ char32_t chr)
160 {
161 return
162 (0x0300 <= chr && chr < 0x0370) ||
163 (0x1dc0 <= chr && chr < 0x1e00) ||
164 (0x20d0 <= chr && chr < 0x2100) ||
165 (0xfe20 <= chr && chr < 0xfe30);
166 }
167
173 template <class T>
174 inline size_t islbreak(_In_ T chr)
175 {
176 return chr == '\n' || chr == '\r';
177 }
178
185 template <class T>
186 inline size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
187 {
188 _Assume_(chr || !count);
189 if (count >= 2 && ((chr[0] == '\r' && chr[1] == '\n') || (chr[0] == '\n' && chr[1] == '\r')))
190 return 2;
191 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
192 return 1;
193 return 0;
194 }
195
202 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
203 {
204 _Assume_(glyph || !count);
205 if (count) {
206#ifdef _WIN32
207 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
208#else
209 size_t i = 1;
210#endif
211 for (; i < count && iscombining(glyph[i]); ++i);
212 return i;
213 }
214 return 0;
215 }
216
224 template <class T>
225 inline size_t strlen(_In_z_ const T* str)
226 {
227 _Assume_(str);
228 size_t i;
229 for (i = 0; str[i]; ++i);
230 return i;
231 }
232
241 template <class T>
242 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
243 {
244 _Assume_(str || !count);
245 size_t i;
246 for (i = 0; i < count && str[i]; ++i);
247 return i;
248 }
249
250 constexpr auto npos{ static_cast<size_t>(-1) };
251
260 template <class T>
261 inline size_t strchr(_In_z_ const T* str, _In_ T chr)
262 {
263 _Assume_(str);
264 for (size_t i = 0; str[i]; ++i)
265 if (str[i] == chr) return i;
266 return npos;
267 }
268
278 template <class T>
279 inline size_t strnchr(
280 _In_reads_or_z_opt_(count) const T* str,
281 _In_ size_t count,
282 _In_ T chr)
283 {
284 _Assume_(str || !count);
285 for (size_t i = 0; i < count && str[i]; ++i)
286 if (str[i] == chr) return i;
287 return npos;
288 }
289
299 template <class T>
300 inline size_t strrnchr(
301 _In_reads_or_z_opt_(count) const T* str,
302 _In_ size_t count,
303 _In_ T chr)
304 {
305 _Assume_(str || !count);
306 size_t z = npos;
307 for (size_t i = 0; i < count && str[i]; ++i)
308 if (str[i] == chr) z = i;
309 return z;
310 }
311
321 template <class T>
322 inline size_t strnichr(
323 _In_reads_or_z_opt_(count) const T* str,
324 _In_ size_t count,
325 _In_ T chr,
326 _In_ const std::locale& locale)
327 {
328 _Assume_(str || !count);
329 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
330 chr = ctype.tolower(chr);
331 for (size_t i = 0; i < count && str[i]; ++i)
332 if (ctype.tolower(str[i]) == chr) return i;
333 return npos;
334 }
335
345 template <class T>
346 inline size_t strrnichr(
347 _In_reads_or_z_opt_(count) const T* str,
348 _In_ size_t count,
349 _In_ T chr,
350 _In_ const std::locale& locale)
351 {
352 _Assume_(str || !count);
353 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
354 chr = ctype.tolower(chr);
355 size_t z = npos;
356 for (size_t i = 0; i < count && str[i]; ++i)
357 if (ctype.tolower(str[i]) == chr) z = i;
358 return z;
359 }
360
369 template <class T1, class T2>
370 inline int strcmp(const T1* str1, const T2* str2)
371 {
372 _Assume_(str1 && str2);
373 T1 a; T2 b;
374 for (size_t i = 0; (a = str1[i]) | (b = str2[i]); ++i) {
375 if (a > b) return +1;
376 if (a < b) return -1;
377 }
378 return 0;
379 }
380
391 template <class T1, class T2>
392 inline int strncmp(
393 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
394 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
395 {
396 _Assume_(str1 || !count1);
397 _Assume_(str2 || !count2);
398 size_t i; T1 a; T2 b;
399 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
400 if (a > b) return +1;
401 if (a < b) return -1;
402 }
403 if (i < count1 && str1[i]) return +1;
404 if (i < count2 && str2[i]) return -1;
405 return 0;
406 }
407
417 template <class T1, class T2>
418 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)
419 {
420 _Assume_((str1 && str2) || !count);
421 size_t i; T1 a; T2 b;
422 for (i = 0; i < count && ((a = str1[i]) | (b = str2[i])); ++i) {
423 if (a > b) return +1;
424 if (a < b) return -1;
425 }
426 if (i < count && str1[i]) return +1;
427 if (i < count && str2[i]) return -1;
428 return 0;
429 }
430
441 template <class T>
442 inline int strncoll(
443 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
444 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
445 _In_ const std::locale& locale)
446 {
447 _Assume_(str1 || !count1);
448 _Assume_(str2 || !count2);
449 auto& collate = std::use_facet<std::collate<T>>(locale);
450 return collate.compare(str1, str1 + count1, str2, str2 + count2);
451 }
452
461 template <class T1, class T2>
462 inline int stricmp(_In_z_ const T1* str1, _In_z_ const T2* str2, _In_ const std::locale& locale)
463 {
464 _Assume_(str1);
465 _Assume_(str2);
466 size_t i; T1 a; T2 b;
467 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
468 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
469 for (i = 0; (a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i])); i++) {
470 if (a > b) return +1;
471 if (a < b) return -1;
472 }
473 if (str1[i]) return +1;
474 if (str2[i]) return -1;
475 return 0;
476 }
477
487 template <class T1, class T2>
488 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)
489 {
490 _Assume_(str1 || !count);
491 _Assume_(str2 || !count);
492 size_t i; T1 a; T2 b;
493 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
494 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
495 for (i = 0; i < count && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
496 if (a > b) return +1;
497 if (a < b) return -1;
498 }
499 if (i < count && str1[i]) return +1;
500 if (i < count && str2[i]) return -1;
501 return 0;
502 }
503
514 template <class T1, class T2>
515 inline int strnicmp(
516 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
517 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
518 _In_ const std::locale& locale)
519 {
520 _Assume_(str1 || !count1);
521 _Assume_(str2 || !count2);
522 size_t i; T1 a; T2 b;
523 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
524 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
525 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
526 if (a > b) return +1;
527 if (a < b) return -1;
528 }
529 if (i < count1 && str1[i]) return +1;
530 if (i < count2 && str2[i]) return -1;
531 return 0;
532 }
533
542 template <class T1, class T2>
543 inline size_t strstr(
544 _In_z_ const T1* str,
545 _In_z_ const T2* sample)
546 {
547 _Assume_(str);
548 _Assume_(sample);
549 for (size_t offset = 0;; ++offset) {
550 for (size_t i = offset, j = 0;; ++i, ++j) {
551 if (!sample[j])
552 return offset;
553 if (!str[i])
554 return npos;
555 if (str[i] != sample[j])
556 break;
557 }
558 }
559 }
560
570 template <class T1, class T2>
571 inline size_t strnstr(
572 _In_reads_or_z_opt_(count) const T1* str,
573 _In_ size_t count,
574 _In_z_ const T2* sample)
575 {
576 _Assume_(str || !count);
577 _Assume_(sample);
578 for (size_t offset = 0;; ++offset) {
579 for (size_t i = offset, j = 0;; ++i, ++j) {
580 if (!sample[j])
581 return offset;
582 if (i >= count || !str[i])
583 return npos;
584 if (str[i] != sample[j])
585 break;
586 }
587 }
588 }
589
598 template <class T1, class T2>
599 inline size_t stristr(
600 _In_z_ const T1* str,
601 _In_z_ const T2* sample,
602 _In_ const std::locale& locale)
603 {
604 _Assume_(str);
605 _Assume_(sample);
606 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
607 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
608 for (size_t offset = 0;; ++offset) {
609 for (size_t i = offset, j = 0;; ++i, ++j) {
610 if (!sample[j])
611 return offset;
612 if (!str[i])
613 return npos;
614 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
615 break;
616 }
617 }
618 }
619
629 template <class T1, class T2>
630 inline size_t strnistr(
631 _In_reads_or_z_opt_(count) const T1* str,
632 _In_ size_t count,
633 _In_z_ const T2* sample,
634 _In_ const std::locale& locale)
635 {
636 _Assume_(str || !count);
637 _Assume_(sample);
638 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
639 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
640 for (size_t offset = 0;; ++offset) {
641 for (size_t i = offset, j = 0;; ++i, ++j) {
642 if (!sample[j])
643 return offset;
644 if (i >= count || !str[i])
645 return npos;
646 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
647 break;
648 }
649 }
650 }
651
660 template <class T1, class T2>
661 inline size_t strcpy(
662 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
663 _In_z_ const T2* src)
664 {
665 _Assume_(dst && src);
666 for (size_t i = 0; ; ++i) {
667 if ((dst[i] = src[i]) == 0)
668 return i;
669 }
670 }
671
681 template <class T1, class T2>
682 inline size_t strncpy(
683 _Out_writes_(count) _Post_maybez_ T1* dst,
684 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
685 {
686 _Assume_(dst && src || !count);
687 for (size_t i = 0; ; ++i) {
688 if (i >= count)
689 return i;
690 if ((dst[i] = src[i]) == 0)
691 return i;
692 }
693 }
694
705 template <class T1, class T2>
706 inline size_t strncpy(
707 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
708 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
709 {
710 _Assume_(dst || !count_dst);
711 _Assume_(src || !count_src);
712 for (size_t i = 0; ; ++i)
713 {
714 if (i >= count_dst)
715 return i;
716 if (i >= count_src) {
717 dst[i] = 0;
718 return i;
719 }
720 if ((dst[i] = src[i]) == 0)
721 return i;
722 }
723 }
724
733 template <class T1, class T2>
734 inline size_t strcat(
735 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
736 _In_z_ const T2* src)
737 {
738 _Assume_(dst && src);
739 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
740 if ((dst[j] = src[i]) == 0)
741 return j;
742 }
743 }
744
754 template <class T1, class T2>
755 inline size_t strncat(
756 _Out_writes_(count) _Post_maybez_ T1* dst,
757 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
758 {
759 _Assume_(dst && src || !count);
760 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
761 if (i >= count)
762 return j;
763 if ((dst[j] = src[i]) == 0)
764 return j;
765 }
766 }
767
778 template <class T1, class T2>
779 inline size_t strncat(
780 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
781 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
782 {
783 _Assume_(dst || !count_dst);
784 _Assume_(src || !count_src);
785 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
786 {
787 if (j >= count_dst)
788 return j;
789 if (i >= count_src) {
790 dst[j] = 0;
791 return j;
792 }
793 if ((dst[j] = src[i]) == 0)
794 return j;
795 }
796 }
797
808 template <class T>
809 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
810 {
811 if (!str) _Unlikely_
812 return nullptr;
813 size_t count = strlen(str) + 1;
814 T* dst = new T[count];
815 strncpy(dst, count, str, SIZE_MAX);
816 return dst;
817 }
818
830 template <class T>
831 inline _Ret_z_ T* strndup(
832 _In_reads_or_z_opt_(count) const T* str,
833 _In_ size_t count)
834 {
835 T* dst = new T[count];
836 strncpy(dst, count, str, SIZE_MAX);
837 return dst;
838 }
839
849 template <class T>
850 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
851 {
852 _Assume_(dst);
853 _Assume_(src);
854 size_t i, j;
855 for (i = j = 0; src[j];) {
856 if (src[j] != '\r' || src[j + 1] != '\n')
857 dst[i++] = src[j++];
858 else {
859 dst[i++] = '\n';
860 j += 2;
861 }
862 }
863 dst[i] = 0;
864 return i;
865 }
866
873 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
874 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &dst, _In_z_ const _Elem* src)
875 {
876 _Assume_(src);
877 _Assume_(src != dst.c_str());
878 dst.clear();
879 dst.reserve(strlen(src));
880 for (size_t j = 0; src[j];) {
881 if (src[j] != '\r' || src[j + 1] != '\n')
882 dst += src[j++];
883 else {
884 dst += '\n';
885 j += 2;
886 }
887 }
888 }
889
895 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
896 inline void crlf2nl(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str)
897 {
898 size_t i, j, n;
899 for (i = j = 0, n = str.size(); j < n;) {
900 if (str[j] != '\r' || str[j + 1] != '\n')
901 str[i++] = str[j++];
902 else {
903 str[i++] = '\n';
904 j += 2;
905 }
906 }
907 str.resize(i);
908 }
909
911 template <class T, class T_bin>
912 inline T_bin strtoint(
913 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
914 _Out_opt_ size_t* end,
915 _In_ int radix,
916 _Out_ uint8_t& flags)
917 {
918 _Assume_(str || !count);
919 _Assume_(radix == 0 || 2 <= radix && radix <= 36);
920
921 size_t i = 0;
922 T_bin value = 0, digit,
923 max_ui = (T_bin)-1,
924 max_ui_pre1, max_ui_pre2;
925
926 flags = 0;
927
928 // Skip leading spaces.
929 for (;; ++i) {
930 if (i >= count || !str[i]) goto error;
931 if (!isspace(str[i])) break;
932 }
933
934 // Read the sign.
935 if (str[i] == '+') {
936 flags &= ~0x01;
937 ++i;
938 if (i >= count || !str[i]) goto error;
939 }
940 else if (str[i] == '-') {
941 flags |= 0x01;
942 ++i;
943 if (i >= count || !str[i]) goto error;
944 }
945
946 if (radix == 16) {
947 // On hexadecimal, allow leading 0x.
948 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
949 i += 2;
950 if (i >= count || !str[i]) goto error;
951 }
952 }
953 else if (!radix) {
954 // Autodetect radix.
955 if (str[i] == '0') {
956 ++i;
957 if (i >= count || !str[i]) goto error;
958 if (str[i] == 'x' || str[i] == 'X') {
959 radix = 16;
960 ++i;
961 if (i >= count || !str[i]) goto error;
962 }
963 else
964 radix = 8;
965 }
966 else
967 radix = 10;
968 }
969
970 // We have the radix.
971 max_ui_pre1 = max_ui / (T_bin)radix;
972 max_ui_pre2 = max_ui % (T_bin)radix;
973 for (;;) {
974 if ('0' <= str[i] && str[i] <= '9')
975 digit = (T_bin)str[i] - '0';
976 else if ('A' <= str[i] && str[i] <= 'Z')
977 digit = (T_bin)str[i] - 'A' + '\x0a';
978 else if ('a' <= str[i] && str[i] <= 'z')
979 digit = (T_bin)str[i] - 'a' + '\x0a';
980 else
981 goto error;
982 if (digit >= (T_bin)radix)
983 goto error;
984
985 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
986 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
987 value = value * (T_bin)radix + digit;
988 else {
989 // Overflow!
990 flags |= 0x02;
991 }
992
993 ++i;
994 if (i >= count || !str[i])
995 goto error;
996 }
997
998 error:
999 if (end) *end = i;
1000 return value;
1001 }
1003
1014 template <class T, class T_bin>
1015 T_bin strtoint(
1016 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1017 _Out_opt_ size_t* end,
1018 _In_ int radix)
1019 {
1020 uint8_t flags;
1021 T_bin value;
1022
1023 switch (sizeof(T_bin)) {
1024 case 1:
1025 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
1026 if ((flags & 0x01) && (value & 0x80)) {
1027 // Sign bit is 1 => overflow.
1028 flags |= 0x02;
1029 }
1030 return (flags & 0x02) ?
1031 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
1032 (flags & 0x01) ? -value : value;
1033
1034 case 2:
1035 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
1036 if ((flags & 0x01) && (value & 0x8000)) {
1037 // Sign bit is 1 => overflow.
1038 flags |= 0x02;
1039 }
1040 return (flags & 0x02) ?
1041 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
1042 (flags & 0x01) ? -value : value;
1043
1044 case 4:
1045 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
1046 if ((flags & 0x01) && (value & 0x80000000)) {
1047 // Sign bit is 1 => overflow.
1048 flags |= 0x02;
1049 }
1050 return (flags & 0x02) ?
1051 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
1052 (flags & 0x01) ? -value : value;
1053
1054 case 8:
1055 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
1056 if ((flags & 0x01) && (value & 0x8000000000000000)) {
1057 // Sign bit is 1 => overflow.
1058 flags |= 0x02;
1059 }
1060 return (flags & 0x02) ?
1061 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
1062 (flags & 0x01) ? -value : value;
1063
1064 default:
1065 throw std::invalid_argument("Unsupported bit length");
1066 }
1067 }
1068
1079 template <class T, class T_bin>
1080 inline T_bin strtouint(
1081 _In_reads_or_z_opt_(count) const T* str,
1082 _In_ size_t count,
1083 _Out_opt_ size_t* end,
1084 _In_ int radix)
1085 {
1086 uint8_t flags;
1087 T_bin value;
1088
1089 switch (sizeof(T_bin)) {
1090 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
1091 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
1092 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
1093 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
1094 default: throw std::invalid_argument("Unsupported bit length");
1095 }
1096
1097 return (flags & 0x02) ?
1098 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
1099 (flags & 0x01) ? ~value : value;
1100 }
1101
1112 template <class T>
1113 inline int32_t strto32(
1114 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1115 _Out_opt_ size_t* end,
1116 _In_ int radix)
1117 {
1118 return strtoint<T, int32_t>(str, count, end, radix);
1119 }
1120
1131 template <class T>
1132 inline int64_t strto64(
1133 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1134 _Out_opt_ size_t* end,
1135 _In_ int radix)
1136 {
1137 return strtoint<T, int64_t>(str, count, end, radix);
1138 }
1139
1151 template <class T>
1152 inline intptr_t strtoi(
1153 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1154 _Out_opt_ size_t* end,
1155 _In_ int radix)
1156 {
1157#if defined(_WIN64) || defined(__LP64__)
1158 return (intptr_t)strto64(str, count, end, radix);
1159#else
1160 return (intptr_t)strto32(str, count, end, radix);
1161#endif
1162 }
1163
1174 template <class T>
1175 inline uint32_t strtou32(
1176 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1177 _Out_opt_ size_t* end,
1178 _In_ int radix)
1179 {
1180 return strtouint<T, uint32_t>(str, count, end, radix);
1181 }
1182
1193 template <class T>
1194 inline uint64_t strtou64(
1195 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1196 _Out_opt_ size_t* end,
1197 _In_ int radix)
1198 {
1199 return strtouint<T, uint64_t>(str, count, end, radix);
1200 }
1201
1213 template <class T>
1214 inline size_t strtoui(
1215 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1216 _Out_opt_ size_t* end,
1217 _In_ int radix)
1218 {
1219#if defined(_WIN64) || defined(__LP64__)
1220 return (size_t)strtou64(str, count, end, radix);
1221#else
1222 return (size_t)strtou32(str, count, end, radix);
1223#endif
1224 }
1225
1227 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)
1228 {
1229 int r;
1230#ifdef _WIN32
1231 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1232#pragma warning(suppress: 4996)
1233 r = _vsnprintf_l(str, capacity, format, locale, arg);
1234#else
1235 r = ::vsnprintf(str, capacity, format, arg);
1236#endif
1237 if (r == -1 && strnlen(str, capacity) == capacity) {
1238 // Buffer overrun. Estimate buffer size for the next iteration.
1239 capacity += std::max<size_t>(capacity / 8, 0x80);
1240 if (capacity > INT_MAX)
1241 throw std::invalid_argument("string too big");
1242 return (int)capacity;
1243 }
1244 return r;
1245 }
1246
1247 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)
1248 {
1249 int r;
1250#ifdef _WIN32
1251 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1252#pragma warning(suppress: 4996)
1253 r = _vsnwprintf_l(str, capacity, format, locale, arg);
1254#else
1255 r = vswprintf(str, capacity, format, arg);
1256#endif
1257 if (r == -1 && strnlen(str, capacity) == capacity) {
1258 // Buffer overrun. Estimate buffer size for the next iteration.
1259 capacity += std::max<size_t>(capacity / 8, 0x80);
1260 if (capacity > INT_MAX)
1261 throw std::invalid_argument("string too big");
1262 return (int)capacity;
1263 }
1264 return r;
1265 }
1267
1276 template<class _Elem, class _Traits, class _Ax>
1277 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)
1278 {
1279 _Elem buf[1024/sizeof(_Elem)];
1280
1281 // Try with stack buffer first.
1282 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
1283 if (count >= 0) {
1284 // Copy from stack.
1285 str.append(buf, count);
1286 } else {
1287 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1288 // Allocate on heap and retry.
1289 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1290 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
1291 if (count >= 0) {
1292 str.append(buf_dyn.get(), count);
1293 break;
1294 }
1295 }
1296 }
1297 }
1298
1306 template<class _Elem, class _Traits, class _Ax>
1307 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, ...)
1308 {
1309 va_list arg;
1310 va_start(arg, locale);
1311 vappendf(str, format, locale, arg);
1312 va_end(arg);
1313 }
1314
1323 template<class _Elem, class _Traits, class _Ax>
1324 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)
1325 {
1326 str.clear();
1327 vappendf(str, format, locale, arg);
1328 }
1329
1337 template<class _Elem, class _Traits, class _Ax>
1338 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, ...)
1339 {
1340 va_list arg;
1341 va_start(arg, locale);
1342 vsprintf(str, format, locale, arg);
1343 va_end(arg);
1344 }
1345
1355 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1356 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)
1357 {
1358 std::basic_string<_Elem, _Traits, _Ax> str;
1359 vappendf(str, format, locale, arg);
1360 return str;
1361 }
1362
1371 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1372 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1373 {
1374 va_list arg;
1375 va_start(arg, locale);
1376 auto str = vsprintf(format, locale, arg);
1377 va_end(arg);
1378 return str;
1379 }
1380
1382 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)
1383 {
1384#ifdef _WIN32
1385 return _strftime_l(str, capacity, format, time, locale);
1386#else
1387 return strftime_l(str, capacity, format, time, locale);
1388#endif
1389 }
1390
1391 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)
1392 {
1393#ifdef _WIN32
1394 return _wcsftime_l(str, capacity, format, time, locale);
1395#else
1396 return wcsftime_l(str, capacity, format, time, locale);
1397#endif
1398 }
1400
1409 template<class _Elem, class _Traits, class _Ax>
1410 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)
1411 {
1412 _Elem buf[1024/sizeof(_Elem)];
1413
1414 // Try with stack buffer first.
1415 size_t count = strftime(buf, _countof(buf), format, time, locale);
1416 if (count) {
1417 // Copy from stack.
1418 str.append(buf, count);
1419 } else {
1420 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1421 // Allocate on heap and retry.
1422 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1423 count = strftime(buf_dyn.get(), capacity, format, time, locale);
1424 if (count) {
1425 str.append(buf_dyn.get(), count);
1426 break;
1427 }
1428 }
1429 }
1430 }
1431
1440 template<class _Elem, class _Traits, class _Ax>
1441 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)
1442 {
1443 str.clear();
1444 strcatftime(str, format, time, locale);
1445 }
1446
1457 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1458 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)
1459 {
1460 std::basic_string<_Elem, _Traits, _Ax> str;
1461 strcatftime(str, format, time, locale);
1462 return str;
1463 }
1464
1471 inline void uuidtostr(_Out_writes_z_(39) char str[39], _In_ const uuid_t& id)
1472 {
1473 _Assume_(str);
1474 _snprintf_s_l(str, 39, _TRUNCATE, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", NULL,
1475#ifdef _WIN32
1476 id.Data1,
1477 static_cast<unsigned int>(id.Data2),
1478 static_cast<unsigned int>(id.Data3),
1479 static_cast<unsigned int>(id.Data4[0]), static_cast<unsigned int>(id.Data4[1]),
1480 static_cast<unsigned int>(id.Data4[2]), static_cast<unsigned int>(id.Data4[3]), static_cast<unsigned int>(id.Data4[4]), static_cast<unsigned int>(id.Data4[5]), static_cast<unsigned int>(id.Data4[6]), static_cast<unsigned int>(id.Data4[7]));
1481#else
1482 *reinterpret_cast<const uint32_t*>(&id[0]),
1483 static_cast<unsigned int>(*reinterpret_cast<const uint16_t*>(&id[4])),
1484 static_cast<unsigned int>(*reinterpret_cast<const uint16_t*>(&id[6])),
1485 static_cast<unsigned int>(id[8]), static_cast<unsigned int>(id[9]),
1486 static_cast<unsigned int>(id[10])), static_cast<unsigned int>(id[11]), static_cast<unsigned int>(id[12]), static_cast<unsigned int>(id)), static_cast<unsigned int>(id[14]), static_cast<unsigned int>(id[15]));
1487#endif
1488 }
1489
1496 inline void uuidtostr(_Out_writes_z_(39) wchar_t str[39], _In_ const uuid_t& id)
1497 {
1498 _Assume_(str);
1499 _snwprintf_s_l(str, 39, _TRUNCATE, L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}", NULL,
1500#ifdef _WIN32
1501 id.Data1,
1502 static_cast<unsigned int>(id.Data2),
1503 static_cast<unsigned int>(id.Data3),
1504 static_cast<unsigned int>(id.Data4[0]), static_cast<unsigned int>(id.Data4[1]),
1505 static_cast<unsigned int>(id.Data4[2]), static_cast<unsigned int>(id.Data4[3]), static_cast<unsigned int>(id.Data4[4]), static_cast<unsigned int>(id.Data4[5]), static_cast<unsigned int>(id.Data4[6]), static_cast<unsigned int>(id.Data4[7]));
1506#else
1507 *reinterpret_cast<const uint32_t*>(&id[0]),
1508 static_cast<unsigned int>(*reinterpret_cast<const uint16_t*>(&id[4])),
1509 static_cast<unsigned int>(*reinterpret_cast<const uint16_t*>(&id[6])),
1510 static_cast<unsigned int>(id[8]), static_cast<unsigned int>(id[9]),
1511 static_cast<unsigned int>(id[10])), static_cast<unsigned int>(id[11]), static_cast<unsigned int>(id[12]), static_cast<unsigned int>(id)), static_cast<unsigned int>(id[14]), static_cast<unsigned int>(id[15]));
1512#endif
1513 }
1514
1522 template<class T>
1523 inline void strlwr(_Inout_z_ T* str, _In_ const std::locale& locale)
1524 {
1525 _Assume_(str);
1526 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1527 for (size_t i = 0; str[i]; ++i)
1528 str[i] = ctype.tolower(str[i]);
1529 }
1530
1539 template<class T>
1540 inline void strlwr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1541 {
1542 _Assume_(str || !count);
1543 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1544 for (size_t i = 0; i < count && str[i]; ++i)
1545 str[i] = ctype.tolower(str[i]);
1546 }
1547
1555 template<class T>
1556 inline void strupr(_Inout_z_ T* str, _In_ const std::locale& locale)
1557 {
1558 _Assume_(str);
1559 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1560 for (size_t i = 0; str[i]; ++i)
1561 str[i] = ctype.toupper(str[i]);
1562 }
1563
1572 template<class T>
1573 inline void strupr(_Inout_updates_z_(count) T* str, _In_ size_t count, _In_ const std::locale& locale)
1574 {
1575 _Assume_(str || !count);
1576 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1577 for (size_t i = 0; i < count && str[i]; ++i)
1578 str[i] = ctype.toupper(str[i]);
1579 }
1580
1588 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1589 inline void strupr(_Inout_ std::basic_string<_Elem, _Traits, _Ax>& str, _In_ const std::locale& locale)
1590 {
1591 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1592 for (size_t i = 0; i < str.size(); ++i)
1593 str[i] = ctype.toupper(str[i]);
1594 }
1595
1604 template<class T>
1605 inline size_t ltrim(
1606 _Inout_z_count_(count) T* str, _In_ size_t count,
1607 _In_ const std::locale& locale)
1608 {
1609 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1610 for (size_t i = 0;; ++i) {
1611 if (i >= count) {
1612 if (count) str[0] = 0;
1613 return 0;
1614 }
1615 if (!str[i]) {
1616 str[0] = 0;
1617 return 0;
1618 }
1619 if (!ctype.is(ctype.space, str[i])) {
1620 if (!i)
1621 return strnlen(str, count);
1622 size_t n = count != SIZE_MAX ? strncpy(str, str + i, count - i) : strcpy(str, str + i);
1623 str[n] = 0;
1624 return n;
1625 }
1626 }
1627 }
1628
1634 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1635 inline void ltrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1636 {
1637 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1638 s.erase(
1639 s.begin(),
1640 std::find_if(
1641 s.begin(),
1642 s.end(),
1643 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }));
1644 }
1645
1654 template<class T>
1655 inline size_t rtrim(
1656 _Inout_z_count_(count) T* str, _In_ size_t count,
1657 _In_ const std::locale& locale)
1658 {
1659 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
1660 for (size_t i = 0, j = 0;;) {
1661 if (i >= count || !str[i]) {
1662 if (j < count) str[j] = 0;
1663 return j;
1664 }
1665 if (!ctype.is(ctype.space, str[i]))
1666 j = ++i;
1667 else
1668 ++i;
1669 }
1670 }
1671
1677 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1678 static inline void rtrim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1679 {
1680 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1681 s.erase(
1682 std::find_if(
1683 s.rbegin(),
1684 s.rend(),
1685 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
1686 s.end());
1687 }
1688
1697 template<class T>
1698 inline size_t trim(
1699 _Inout_z_count_(count) T* str, _In_ size_t count,
1700 _In_ const std::locale& locale)
1701 {
1702 return ltrim(str, rtrim(str, count, locale), locale);
1703 }
1704
1710 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1711 static inline void trim(_Inout_ std::basic_string<_Elem, _Traits, _Ax> &s, _In_ const std::locale& locale)
1712 {
1713 const auto& ctype = std::use_facet<std::ctype<_Elem>>(locale);
1714 s.erase(
1715 s.begin(),
1716 std::find_if(
1717 s.begin(),
1718 s.end(),
1719 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }));
1720 s.erase(
1721 std::find_if(
1722 s.rbegin(),
1723 s.rend(),
1724 [&](_Elem ch) { return !ctype.is(ctype.space, ch); }).base(),
1725 s.end());
1726 }
1727}
Deleter for unique_ptr using free_locale.
Definition string.hpp:62
void operator()(locale_t locale) const
Delete a pointer.
Definition string.hpp:66