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 <assert.h>
10#include <ctype.h>
11#include <locale.h>
12#include <stdarg.h>
13#include <stdint.h>
14#include <stdio.h>
15#include <stdarg.h>
16#ifdef __APPLE__
17#include <xlocale.h>
18#endif
19#include <locale>
20#include <memory>
21#include <stdexcept>
22
23namespace stdex
24{
25#ifdef _WIN32
26 using locale_t = _locale_t;
27
28 inline locale_t create_locale(_In_ int category, _In_z_ const char* locale) { return _create_locale(category, locale); }
29 inline locale_t create_locale(_In_ int category, _In_z_ const wchar_t* locale) { return _wcreate_locale(category, locale); }
30 inline void free_locale(_In_opt_ _locale_t locale) { _free_locale(locale); }
31#else
32 using locale_t = ::locale_t;
33#endif
34
39 {
43 void operator()(_In_ locale_t locale) const
44 {
45#ifdef _WIN32
46 free_locale(locale);
47#else
48 freelocale(locale);
49#endif
50 }
51 };
52
56#if defined(_WIN32)
57 using locale = std::unique_ptr<__crt_locale_pointers, free_locale_delete>;
58#elif defined(__APPLE__)
59 using locale = std::unique_ptr<struct _xlocale, free_locale_delete>;
60#else
61 using locale = std::unique_ptr<struct __locale_struct, free_locale_delete>;
62#endif
63
67#if defined(_WIN32)
68 static locale locale_C(_create_locale(LC_ALL, "C"));
69#elif defined(__APPLE__)
70 static locale locale_C(newlocale(LC_ALL_MASK, "C", LC_GLOBAL_LOCALE));
71#else
72#error TODO
73#endif
74
78#ifdef _WIN32
79 typedef wchar_t utf16_t;
80#else
81 typedef char16_t utf16_t;
82#endif
83
89 inline bool is_high_surrogate(_In_ utf16_t chr)
90 {
91 return 0xd800 < chr && chr < 0xdc00;
92 }
93
99 inline bool is_low_surrogate(_In_ utf16_t chr)
100 {
101 return 0xdc00 < chr && chr < 0xe000;
102 }
103
109 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
110 {
111 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
112 }
113
119 inline char32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
120 {
121 assert(is_surrogate_pair(str));
122 return
123 ((char32_t)(str[0] - 0xd800) << 10) +
124 (char32_t)(str[1] - 0xdc00) +
125 0x10000;
126 }
127
133 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ char32_t chr)
134 {
135 assert(chr >= 0x10000);
136 chr -= 0x10000;
137 str[0] = 0xd800 + (char32_t)((chr >> 10) & 0x3ff);
138 str[1] = 0xdc00 + (char32_t)(chr & 0x3ff);
139 }
140
146 inline bool iscombining(_In_ char32_t chr)
147 {
148 return
149 (0x0300 <= chr && chr < 0x0370) ||
150 (0x1dc0 <= chr && chr < 0x1e00) ||
151 (0x20d0 <= chr && chr < 0x2100) ||
152 (0xfe20 <= chr && chr < 0xfe30);
153 }
154
160 template <class T>
161 inline size_t islbreak(_In_ T chr)
162 {
163 return chr == '\n' || chr == '\r';
164 }
165
172 template <class T>
173 inline size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
174 {
175 _Analysis_assume_(chr || !count);
176 if (count >= 2 && ((chr[0] == '\r' && chr[1] == '\n') || (chr[0] == '\n' && chr[1] == '\r')))
177 return 2;
178 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
179 return 1;
180 return 0;
181 }
182
189 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
190 {
191 _Analysis_assume_(glyph || !count);
192 if (count) {
193#ifdef _WIN32
194 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
195#else
196 size_t i = 1;
197#endif
198 for (; i < count && iscombining(glyph[i]); ++i);
199 return i;
200 }
201 return 0;
202 }
203
211 template <class T>
212 inline size_t strlen(_In_z_ const T* str)
213 {
214 assert(str);
215 size_t i;
216 for (i = 0; str[i]; ++i);
217 return i;
218 }
219
228 template <class T>
229 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
230 {
231 assert(str || !count);
232 size_t i;
233 for (i = 0; i < count && str[i]; ++i);
234 return i;
235 }
236
237 constexpr auto npos{ static_cast<size_t>(-1) };
238
247 template <class T>
248 inline size_t strchr(_In_z_ const T* str, _In_ T chr)
249 {
250 assert(str);
251 for (size_t i = 0; str[i]; ++i)
252 if (str[i] == chr) return i;
253 return npos;
254 }
255
265 template <class T>
266 inline size_t strnchr(
267 _In_reads_or_z_opt_(count) const T* str,
268 _In_ size_t count,
269 _In_ T chr)
270 {
271 assert(str || !count);
272 for (size_t i = 0; i < count && str[i]; ++i)
273 if (str[i] == chr) return i;
274 return npos;
275 }
276
286 template <class T>
287 inline size_t strrnchr(
288 _In_reads_or_z_opt_(count) const T* str,
289 _In_ size_t count,
290 _In_ T chr)
291 {
292 assert(str || !count);
293 size_t z = npos;
294 for (size_t i = 0; i < count && str[i]; ++i)
295 if (str[i] == chr) z = i;
296 return z;
297 }
298
308 template <class T>
309 inline size_t strnichr(
310 _In_reads_or_z_opt_(count) const T* str,
311 _In_ size_t count,
312 _In_ T chr,
313 _In_ const std::locale& locale)
314 {
315 assert(str || !count);
316 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
317 chr = ctype.tolower(chr);
318 for (size_t i = 0; i < count && str[i]; ++i)
319 if (ctype.tolower(str[i]) == chr) return i;
320 return npos;
321 }
322
332 template <class T>
333 inline size_t strrnichr(
334 _In_reads_or_z_opt_(count) const T* str,
335 _In_ size_t count,
336 _In_ T chr,
337 _In_ const std::locale& locale)
338 {
339 assert(str || !count);
340 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
341 chr = ctype.tolower(chr);
342 size_t z = npos;
343 for (size_t i = 0; i < count && str[i]; ++i)
344 if (ctype.tolower(str[i]) == chr) z = i;
345 return z;
346 }
347
358 template <class T1, class T2>
359 inline int strncmp(
360 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
361 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
362 {
363 assert(str1 || !count1);
364 assert(str2 || !count2);
365 size_t i; T1 a; T2 b;
366 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
367 if (a > b) return +1;
368 if (a < b) return -1;
369 }
370 if (i < count1 && str1[i]) return +1;
371 if (i < count2 && str2[i]) return -1;
372 return 0;
373 }
374
385 template <class T>
386 inline int strncoll(
387 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
388 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
389 _In_ const std::locale& locale)
390 {
391 assert(str1 || !count1);
392 assert(str2 || !count2);
393 auto& collate = std::use_facet<std::collate<T>>(locale);
394 return collate.compare(str1, str1 + count1, str2, str2 + count2);
395 }
396
407 template <class T1, class T2>
408 inline int strnicmp(
409 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
410 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
411 _In_ const std::locale& locale)
412 {
413 assert(str1 || !count1);
414 assert(str2 || !count2);
415 size_t i; T1 a; T2 b;
416 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
417 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
418 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
419 if (a > b) return +1;
420 if (a < b) return -1;
421 }
422 if (i < count1 && str1[i]) return +1;
423 if (i < count2 && str2[i]) return -1;
424 return 0;
425 }
426
435 template <class T1, class T2>
436 inline size_t strstr(
437 _In_z_ const T1* str,
438 _In_z_ const T2* sample)
439 {
440 assert(str);
441 assert(sample);
442 for (size_t offset = 0;; ++offset) {
443 for (size_t i = offset, j = 0;; ++i, ++j) {
444 if (!sample[j])
445 return offset;
446 if (!str[i])
447 return npos;
448 if (str[i] != sample[j])
449 break;
450 }
451 }
452 }
453
463 template <class T1, class T2>
464 inline size_t strnstr(
465 _In_reads_or_z_opt_(count) const T1* str,
466 _In_ size_t count,
467 _In_z_ const T2* sample)
468 {
469 assert(str || !count);
470 assert(sample);
471 for (size_t offset = 0;; ++offset) {
472 for (size_t i = offset, j = 0;; ++i, ++j) {
473 if (!sample[j])
474 return offset;
475 if (i >= count || !str[i])
476 return npos;
477 if (str[i] != sample[j])
478 break;
479 }
480 }
481 }
482
491 template <class T1, class T2>
492 inline size_t stristr(
493 _In_z_ const T1* str,
494 _In_z_ const T2* sample,
495 _In_ const std::locale& locale)
496 {
497 assert(str);
498 assert(sample);
499 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
500 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
501 for (size_t offset = 0;; ++offset) {
502 for (size_t i = offset, j = 0;; ++i, ++j) {
503 if (!sample[j])
504 return offset;
505 if (!str[i])
506 return npos;
507 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
508 break;
509 }
510 }
511 }
512
522 template <class T1, class T2>
523 inline size_t strnistr(
524 _In_reads_or_z_opt_(count) const T1* str,
525 _In_ size_t count,
526 _In_z_ const T2* sample,
527 _In_ const std::locale& locale)
528 {
529 assert(str || !count);
530 assert(sample);
531 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
532 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
533 for (size_t offset = 0;; ++offset) {
534 for (size_t i = offset, j = 0;; ++i, ++j) {
535 if (!sample[j])
536 return offset;
537 if (i >= count || !str[i])
538 return npos;
539 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
540 break;
541 }
542 }
543 }
544
553 template <class T1, class T2>
554 inline size_t strcpy(
555 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
556 _In_z_ const T2* src)
557 {
558 assert(dst && src);
559 for (size_t i = 0; ; ++i) {
560 if ((dst[i] = src[i]) == 0)
561 return i;
562 }
563 }
564
574 template <class T1, class T2>
575 inline size_t strncpy(
576 _Out_writes_(count) _Post_maybez_ T1* dst,
577 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
578 {
579 assert(dst && src || !count);
580 for (size_t i = 0; ; ++i) {
581 if (i >= count)
582 return i;
583 if ((dst[i] = src[i]) == 0)
584 return i;
585 }
586 }
587
598 template <class T1, class T2>
599 inline size_t strncpy(
600 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
601 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
602 {
603 assert(dst || !count_dst);
604 assert(src || !count_src);
605 for (size_t i = 0; ; ++i)
606 {
607 if (i > count_dst)
608 return i;
609 if (i > count_src) {
610 dst[i] = 0;
611 return i;
612 }
613 if ((dst[i] = src[i]) == 0)
614 return i;
615 }
616 }
617
626 template <class T1, class T2>
627 inline size_t strcat(
628 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
629 _In_z_ const T2* src)
630 {
631 assert(dst && src);
632 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
633 if ((dst[j] = src[i]) == 0)
634 return j;
635 }
636 }
637
647 template <class T1, class T2>
648 inline size_t strncat(
649 _Out_writes_(count) _Post_maybez_ T1* dst,
650 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
651 {
652 assert(dst && src || !count);
653 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
654 if (i >= count)
655 return j;
656 if ((dst[j] = src[i]) == 0)
657 return j;
658 }
659 }
660
671 template <class T1, class T2>
672 inline size_t strncat(
673 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
674 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
675 {
676 assert(dst || !count_dst);
677 assert(src || !count_src);
678 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
679 {
680 if (j > count_dst)
681 return j;
682 if (i > count_src) {
683 dst[j] = 0;
684 return j;
685 }
686 if ((dst[j] = src[i]) == 0)
687 return j;
688 }
689 }
690
701 template <class T>
702 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
703 {
704 if (!str) _Unlikely_
705 return nullptr;
706 size_t count = strlen(str) + 1;
707 T* dst = new T[count];
708 strncpy(dst, count, str, SIZE_MAX);
709 return dst;
710 }
711
723 template <class T>
724 inline _Ret_z_ T* strndup(
725 _In_reads_or_z_opt_(count) const T* str,
726 _In_ size_t count)
727 {
728 T* dst = new T[count];
729 strncpy(dst, count, str, SIZE_MAX);
730 return dst;
731 }
732
742 template <class T>
743 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
744 {
745 assert(dst);
746 assert(src);
747 size_t i, j;
748 for (i = j = 0; src[j];) {
749 if (src[j] != '\r' || src[j + 1] != '\n')
750 dst[i++] = src[j++];
751 else {
752 dst[i++] = '\n';
753 j += 2;
754 }
755 }
756 dst[i] = 0;
757 return i;
758 }
759
761 template <class T, class T_bin>
762 inline T_bin strtoint(
763 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
764 _Out_opt_ size_t* end,
765 _In_ int radix,
766 _Out_ uint8_t& flags)
767 {
768 assert(str || !count);
769 assert(radix == 0 || 2 <= radix && radix <= 36);
770
771 size_t i = 0;
772 T_bin value = 0, digit,
773 max_ui = (T_bin)-1,
774 max_ui_pre1, max_ui_pre2;
775
776 flags = 0;
777
778 // Skip leading spaces.
779 for (;; ++i) {
780 if (i >= count || !str[i]) goto error;
781 if (!isspace(str[i])) break;
782 }
783
784 // Read the sign.
785 if (str[i] == '+') {
786 flags &= ~0x01;
787 ++i;
788 if (i >= count || !str[i]) goto error;
789 }
790 else if (str[i] == '-') {
791 flags |= 0x01;
792 ++i;
793 if (i >= count || !str[i]) goto error;
794 }
795
796 if (radix == 16) {
797 // On hexadecimal, allow leading 0x.
798 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
799 i += 2;
800 if (i >= count || !str[i]) goto error;
801 }
802 }
803 else if (!radix) {
804 // Autodetect radix.
805 if (str[i] == '0') {
806 ++i;
807 if (i >= count || !str[i]) goto error;
808 if (str[i] == 'x' || str[i] == 'X') {
809 radix = 16;
810 ++i;
811 if (i >= count || !str[i]) goto error;
812 }
813 else
814 radix = 8;
815 }
816 else
817 radix = 10;
818 }
819
820 // We have the radix.
821 max_ui_pre1 = max_ui / (T_bin)radix;
822 max_ui_pre2 = max_ui % (T_bin)radix;
823 for (;;) {
824 if ('0' <= str[i] && str[i] <= '9')
825 digit = (T_bin)str[i] - '0';
826 else if ('A' <= str[i] && str[i] <= 'Z')
827 digit = (T_bin)str[i] - 'A' + '\x0a';
828 else if ('a' <= str[i] && str[i] <= 'z')
829 digit = (T_bin)str[i] - 'a' + '\x0a';
830 else
831 goto error;
832 if (digit >= (T_bin)radix)
833 goto error;
834
835 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
836 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
837 value = value * (T_bin)radix + digit;
838 else {
839 // Overflow!
840 flags |= 0x02;
841 }
842
843 ++i;
844 if (i >= count || !str[i])
845 goto error;
846 }
847
848 error:
849 if (end) *end = i;
850 return value;
851 }
853
864 template <class T, class T_bin>
865 T_bin strtoint(
866 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
867 _Out_opt_ size_t* end,
868 _In_ int radix)
869 {
870 uint8_t flags;
871 T_bin value;
872
873 switch (sizeof(T_bin)) {
874 case 1:
875 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
876 if ((flags & 0x01) && (value & 0x80)) {
877 // Sign bit is 1 => overflow.
878 flags |= 0x02;
879 }
880 return (flags & 0x02) ?
881 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
882 (flags & 0x01) ? -value : value;
883
884 case 2:
885 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
886 if ((flags & 0x01) && (value & 0x8000)) {
887 // Sign bit is 1 => overflow.
888 flags |= 0x02;
889 }
890 return (flags & 0x02) ?
891 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
892 (flags & 0x01) ? -value : value;
893
894 case 4:
895 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
896 if ((flags & 0x01) && (value & 0x80000000)) {
897 // Sign bit is 1 => overflow.
898 flags |= 0x02;
899 }
900 return (flags & 0x02) ?
901 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
902 (flags & 0x01) ? -value : value;
903
904 case 8:
905 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
906 if ((flags & 0x01) && (value & 0x8000000000000000)) {
907 // Sign bit is 1 => overflow.
908 flags |= 0x02;
909 }
910 return (flags & 0x02) ?
911 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
912 (flags & 0x01) ? -value : value;
913
914 default:
915 throw std::invalid_argument("Unsupported bit length");
916 }
917 }
918
929 template <class T, class T_bin>
930 inline T_bin strtouint(
931 _In_reads_or_z_opt_(count) const T* str,
932 _In_ size_t count,
933 _Out_opt_ size_t* end,
934 _In_ int radix)
935 {
936 uint8_t flags;
937 T_bin value;
938
939 switch (sizeof(T_bin)) {
940 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
941 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
942 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
943 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
944 default: throw std::invalid_argument("Unsupported bit length");
945 }
946
947 return (flags & 0x02) ?
948 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
949 (flags & 0x01) ? ~value : value;
950 }
951
962 template <class T>
963 inline int32_t strto32(
964 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
965 _Out_opt_ size_t* end,
966 _In_ int radix)
967 {
968 return strtoint<T, int32_t>(str, count, end, radix);
969 }
970
981 template <class T>
982 inline int64_t strto64(
983 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
984 _Out_opt_ size_t* end,
985 _In_ int radix)
986 {
987 return strtoint<T, int64_t>(str, count, end, radix);
988 }
989
1001 template <class T>
1002 inline intptr_t strtoi(
1003 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1004 _Out_opt_ size_t* end,
1005 _In_ int radix)
1006 {
1007#if defined(_WIN64) || defined(__LP64__)
1008 return (intptr_t)strto64(str, count, end, radix);
1009#else
1010 return (intptr_t)strto32(str, count, end, radix);
1011#endif
1012 }
1013
1024 template <class T>
1025 inline uint32_t strtou32(
1026 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1027 _Out_opt_ size_t* end,
1028 _In_ int radix)
1029 {
1030 return strtouint<T, uint32_t>(str, count, end, radix);
1031 }
1032
1043 template <class T>
1044 inline uint64_t strtou64(
1045 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1046 _Out_opt_ size_t* end,
1047 _In_ int radix)
1048 {
1049 return strtouint<T, uint64_t>(str, count, end, radix);
1050 }
1051
1063 template <class T>
1064 inline size_t strtoui(
1065 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1066 _Out_opt_ size_t* end,
1067 _In_ int radix)
1068 {
1069#if defined(_WIN64) || defined(__LP64__)
1070 return (size_t)strtou64(str, count, end, radix);
1071#else
1072 return (size_t)strtou32(str, count, end, radix);
1073#endif
1074 }
1075
1077 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)
1078 {
1079 int r;
1080#ifdef _WIN32
1081 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1082#pragma warning(suppress: 4996)
1083 r = _vsnprintf_l(str, capacity, format, locale, arg);
1084#else
1085 r = ::vsnprintf(str, capacity, format, arg);
1086#endif
1087 if (r == -1 && strnlen(str, capacity) == capacity) {
1088 // Buffer overrun. Estimate buffer size for the next iteration.
1089 capacity += std::max<size_t>(capacity / 8, 0x80);
1090 if (capacity > INT_MAX)
1091 throw std::invalid_argument("string too big");
1092 return (int)capacity;
1093 }
1094 return r;
1095 }
1096
1097 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)
1098 {
1099 int r;
1100
1101#ifdef _WIN32
1102 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1103#pragma warning(suppress: 4996)
1104 r = _vsnwprintf_l(str, capacity, format, locale, arg);
1105#else
1106 r = vswprintf(str, capacity, format, arg);
1107#endif
1108 if (r == -1 && strnlen(str, capacity) == capacity) {
1109 // Buffer overrun. Estimate buffer size for the next iteration.
1110 capacity += std::max<size_t>(capacity / 8, 0x80);
1111 if (capacity > INT_MAX)
1112 throw std::invalid_argument("string too big");
1113 return (int)capacity;
1114 }
1115 return r;
1116 }
1118
1127 template<class _Elem, class _Traits, class _Ax>
1128 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)
1129 {
1130 _Elem buf[1024/sizeof(_Elem)];
1131
1132 // Try with stack buffer first.
1133 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
1134 if (count >= 0) {
1135 // Copy from stack.
1136 str.append(buf, count);
1137 } else {
1138 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1139 // Allocate on heap and retry.
1140 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1141 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
1142 if (count >= 0) {
1143 str.append(buf_dyn.get(), count);
1144 break;
1145 }
1146 }
1147 }
1148 }
1149
1157 template<class _Elem, class _Traits, class _Ax>
1158 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, ...)
1159 {
1160 va_list arg;
1161 va_start(arg, locale);
1162 vappendf(str, format, locale, arg);
1163 va_end(arg);
1164 }
1165
1174 template<class _Elem, class _Traits, class _Ax>
1175 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)
1176 {
1177 str.clear();
1178 vappendf(str, format, locale, arg);
1179 }
1180
1188 template<class _Elem, class _Traits, class _Ax>
1189 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, ...)
1190 {
1191 va_list arg;
1192 va_start(arg, locale);
1193 vsprintf(str, format, locale, arg);
1194 va_end(arg);
1195 }
1196
1206 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1207 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)
1208 {
1209 std::basic_string<_Elem, _Traits, _Ax> str;
1210 vappendf(str, format, locale, arg);
1211 return str;
1212 }
1213
1222 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1223 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1224 {
1225 va_list arg;
1226 va_start(arg, locale);
1227 auto str = vsprintf(format, locale, arg);
1228 va_end(arg);
1229 return str;
1230 }
1231}
Deleter for unique_ptr using free_locale.
Definition string.hpp:39
void operator()(locale_t locale) const
Delete a pointer.
Definition string.hpp:43