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