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
34 inline locale_t create_locale(_In_ int category, _In_z_ const char* locale)
35 {
36 int mask = 0;
37 switch (category) {
38 case LC_ALL : mask = LC_ALL_MASK ; break;
39 case LC_COLLATE : mask = LC_COLLATE_MASK ; break;
40 case LC_CTYPE : mask = LC_CTYPE_MASK ; break;
41 case LC_MESSAGES: mask = LC_MESSAGES_MASK; break;
42 case LC_MONETARY: mask = LC_MONETARY_MASK; break;
43 case LC_NUMERIC : mask = LC_NUMERIC_MASK ; break;
44 case LC_TIME : mask = LC_TIME_MASK ; break;
45 }
46 return newlocale(mask, locale, LC_GLOBAL_LOCALE);
47 }
48
49 inline void free_locale(_In_opt_ locale_t locale) { freelocale(locale); }
50#endif
51
56 {
60 void operator()(_In_ locale_t locale) const
61 {
62 free_locale(locale);
63 }
64 };
65
69#if defined(_WIN32)
70 using locale = std::unique_ptr<__crt_locale_pointers, free_locale_delete>;
71#elif defined(__APPLE__)
72 using locale = std::unique_ptr<struct _xlocale, free_locale_delete>;
73#else
74 using locale = std::unique_ptr<struct __locale_struct, free_locale_delete>;
75#endif
76
80 static locale locale_C(create_locale(LC_ALL, "C"));
81
85#ifdef _WIN32
86 typedef wchar_t utf16_t;
87#else
88 typedef char16_t utf16_t;
89#endif
90
96 inline bool is_high_surrogate(_In_ utf16_t chr)
97 {
98 return 0xd800 < chr && chr < 0xdc00;
99 }
100
106 inline bool is_low_surrogate(_In_ utf16_t chr)
107 {
108 return 0xdc00 < chr && chr < 0xe000;
109 }
110
116 inline bool is_surrogate_pair(_In_reads_(2) const utf16_t* str)
117 {
118 return is_high_surrogate(str[0]) && is_low_surrogate(str[1]);
119 }
120
126 inline char32_t surrogate_pair_to_ucs4(_In_reads_(2) const utf16_t* str)
127 {
128 assert(is_surrogate_pair(str));
129 return
130 ((char32_t)(str[0] - 0xd800) << 10) +
131 (char32_t)(str[1] - 0xdc00) +
132 0x10000;
133 }
134
140 inline void ucs4_to_surrogate_pair(_Out_writes_(2) utf16_t* str, _In_ char32_t chr)
141 {
142 assert(chr >= 0x10000);
143 chr -= 0x10000;
144 str[0] = 0xd800 + (char32_t)((chr >> 10) & 0x3ff);
145 str[1] = 0xdc00 + (char32_t)(chr & 0x3ff);
146 }
147
153 inline bool iscombining(_In_ char32_t chr)
154 {
155 return
156 (0x0300 <= chr && chr < 0x0370) ||
157 (0x1dc0 <= chr && chr < 0x1e00) ||
158 (0x20d0 <= chr && chr < 0x2100) ||
159 (0xfe20 <= chr && chr < 0xfe30);
160 }
161
167 template <class T>
168 inline size_t islbreak(_In_ T chr)
169 {
170 return chr == '\n' || chr == '\r';
171 }
172
179 template <class T>
180 inline size_t islbreak(_In_reads_or_z_opt_(count) const T* chr, _In_ size_t count)
181 {
182 _Analysis_assume_(chr || !count);
183 if (count >= 2 && ((chr[0] == '\r' && chr[1] == '\n') || (chr[0] == '\n' && chr[1] == '\r')))
184 return 2;
185 if (count > 1 && (chr[0] == '\n' || chr[0] == '\r'))
186 return 1;
187 return 0;
188 }
189
196 inline size_t glyphlen(_In_reads_or_z_opt_(count) const wchar_t* glyph, _In_ size_t count)
197 {
198 _Analysis_assume_(glyph || !count);
199 if (count) {
200#ifdef _WIN32
201 size_t i = count < 2 || !is_surrogate_pair(glyph) ? 1 : 2;
202#else
203 size_t i = 1;
204#endif
205 for (; i < count && iscombining(glyph[i]); ++i);
206 return i;
207 }
208 return 0;
209 }
210
218 template <class T>
219 inline size_t strlen(_In_z_ const T* str)
220 {
221 assert(str);
222 size_t i;
223 for (i = 0; str[i]; ++i);
224 return i;
225 }
226
235 template <class T>
236 inline size_t strnlen(_In_reads_or_z_opt_(count) const T* str, _In_ size_t count)
237 {
238 assert(str || !count);
239 size_t i;
240 for (i = 0; i < count && str[i]; ++i);
241 return i;
242 }
243
244 constexpr auto npos{ static_cast<size_t>(-1) };
245
254 template <class T>
255 inline size_t strchr(_In_z_ const T* str, _In_ T chr)
256 {
257 assert(str);
258 for (size_t i = 0; str[i]; ++i)
259 if (str[i] == chr) return i;
260 return npos;
261 }
262
272 template <class T>
273 inline size_t strnchr(
274 _In_reads_or_z_opt_(count) const T* str,
275 _In_ size_t count,
276 _In_ T chr)
277 {
278 assert(str || !count);
279 for (size_t i = 0; i < count && str[i]; ++i)
280 if (str[i] == chr) return i;
281 return npos;
282 }
283
293 template <class T>
294 inline size_t strrnchr(
295 _In_reads_or_z_opt_(count) const T* str,
296 _In_ size_t count,
297 _In_ T chr)
298 {
299 assert(str || !count);
300 size_t z = npos;
301 for (size_t i = 0; i < count && str[i]; ++i)
302 if (str[i] == chr) z = i;
303 return z;
304 }
305
315 template <class T>
316 inline size_t strnichr(
317 _In_reads_or_z_opt_(count) const T* str,
318 _In_ size_t count,
319 _In_ T chr,
320 _In_ const std::locale& locale)
321 {
322 assert(str || !count);
323 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
324 chr = ctype.tolower(chr);
325 for (size_t i = 0; i < count && str[i]; ++i)
326 if (ctype.tolower(str[i]) == chr) return i;
327 return npos;
328 }
329
339 template <class T>
340 inline size_t strrnichr(
341 _In_reads_or_z_opt_(count) const T* str,
342 _In_ size_t count,
343 _In_ T chr,
344 _In_ const std::locale& locale)
345 {
346 assert(str || !count);
347 const auto& ctype = std::use_facet<std::ctype<T>>(locale);
348 chr = ctype.tolower(chr);
349 size_t z = npos;
350 for (size_t i = 0; i < count && str[i]; ++i)
351 if (ctype.tolower(str[i]) == chr) z = i;
352 return z;
353 }
354
365 template <class T1, class T2>
366 inline int strncmp(
367 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
368 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2)
369 {
370 assert(str1 || !count1);
371 assert(str2 || !count2);
372 size_t i; T1 a; T2 b;
373 for (i = 0; i < count1 && i < count2 && ((a = str1[i]) | (b = str2[i])); ++i) {
374 if (a > b) return +1;
375 if (a < b) return -1;
376 }
377 if (i < count1 && str1[i]) return +1;
378 if (i < count2 && str2[i]) return -1;
379 return 0;
380 }
381
392 template <class T>
393 inline int strncoll(
394 _In_reads_or_z_opt_(count1) const T* str1, _In_ size_t count1,
395 _In_reads_or_z_opt_(count2) const T* str2, _In_ size_t count2,
396 _In_ const std::locale& locale)
397 {
398 assert(str1 || !count1);
399 assert(str2 || !count2);
400 auto& collate = std::use_facet<std::collate<T>>(locale);
401 return collate.compare(str1, str1 + count1, str2, str2 + count2);
402 }
403
414 template <class T1, class T2>
415 inline int strnicmp(
416 _In_reads_or_z_opt_(count1) const T1* str1, _In_ size_t count1,
417 _In_reads_or_z_opt_(count2) const T2* str2, _In_ size_t count2,
418 _In_ const std::locale& locale)
419 {
420 assert(str1 || !count1);
421 assert(str2 || !count2);
422 size_t i; T1 a; T2 b;
423 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
424 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
425 for (i = 0; i < count1 && i < count2 && ((a = ctype1.tolower(str1[i])) | (b = ctype2.tolower(str2[i]))); i++) {
426 if (a > b) return +1;
427 if (a < b) return -1;
428 }
429 if (i < count1 && str1[i]) return +1;
430 if (i < count2 && str2[i]) return -1;
431 return 0;
432 }
433
442 template <class T1, class T2>
443 inline size_t strstr(
444 _In_z_ const T1* str,
445 _In_z_ const T2* sample)
446 {
447 assert(str);
448 assert(sample);
449 for (size_t offset = 0;; ++offset) {
450 for (size_t i = offset, j = 0;; ++i, ++j) {
451 if (!sample[j])
452 return offset;
453 if (!str[i])
454 return npos;
455 if (str[i] != sample[j])
456 break;
457 }
458 }
459 }
460
470 template <class T1, class T2>
471 inline size_t strnstr(
472 _In_reads_or_z_opt_(count) const T1* str,
473 _In_ size_t count,
474 _In_z_ const T2* sample)
475 {
476 assert(str || !count);
477 assert(sample);
478 for (size_t offset = 0;; ++offset) {
479 for (size_t i = offset, j = 0;; ++i, ++j) {
480 if (!sample[j])
481 return offset;
482 if (i >= count || !str[i])
483 return npos;
484 if (str[i] != sample[j])
485 break;
486 }
487 }
488 }
489
498 template <class T1, class T2>
499 inline size_t stristr(
500 _In_z_ const T1* str,
501 _In_z_ const T2* sample,
502 _In_ const std::locale& locale)
503 {
504 assert(str);
505 assert(sample);
506 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
507 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
508 for (size_t offset = 0;; ++offset) {
509 for (size_t i = offset, j = 0;; ++i, ++j) {
510 if (!sample[j])
511 return offset;
512 if (!str[i])
513 return npos;
514 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
515 break;
516 }
517 }
518 }
519
529 template <class T1, class T2>
530 inline size_t strnistr(
531 _In_reads_or_z_opt_(count) const T1* str,
532 _In_ size_t count,
533 _In_z_ const T2* sample,
534 _In_ const std::locale& locale)
535 {
536 assert(str || !count);
537 assert(sample);
538 const auto& ctype1 = std::use_facet<std::ctype<T1>>(locale);
539 const auto& ctype2 = std::use_facet<std::ctype<T2>>(locale);
540 for (size_t offset = 0;; ++offset) {
541 for (size_t i = offset, j = 0;; ++i, ++j) {
542 if (!sample[j])
543 return offset;
544 if (i >= count || !str[i])
545 return npos;
546 if (ctype1.tolower(str[i]) != ctype2.tolower(sample[j]))
547 break;
548 }
549 }
550 }
551
560 template <class T1, class T2>
561 inline size_t strcpy(
562 _Out_writes_z_(_String_length_(src) + 1) T1* dst,
563 _In_z_ const T2* src)
564 {
565 assert(dst && src);
566 for (size_t i = 0; ; ++i) {
567 if ((dst[i] = src[i]) == 0)
568 return i;
569 }
570 }
571
581 template <class T1, class T2>
582 inline size_t strncpy(
583 _Out_writes_(count) _Post_maybez_ T1* dst,
584 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
585 {
586 assert(dst && src || !count);
587 for (size_t i = 0; ; ++i) {
588 if (i >= count)
589 return i;
590 if ((dst[i] = src[i]) == 0)
591 return i;
592 }
593 }
594
605 template <class T1, class T2>
606 inline size_t strncpy(
607 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
608 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
609 {
610 assert(dst || !count_dst);
611 assert(src || !count_src);
612 for (size_t i = 0; ; ++i)
613 {
614 if (i > count_dst)
615 return i;
616 if (i > count_src) {
617 dst[i] = 0;
618 return i;
619 }
620 if ((dst[i] = src[i]) == 0)
621 return i;
622 }
623 }
624
633 template <class T1, class T2>
634 inline size_t strcat(
635 _In_z_ _Out_writes_z_(_String_length_(dst) + _String_length_(src) + 1) T1* dst,
636 _In_z_ const T2* src)
637 {
638 assert(dst && src);
639 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
640 if ((dst[j] = src[i]) == 0)
641 return j;
642 }
643 }
644
654 template <class T1, class T2>
655 inline size_t strncat(
656 _Out_writes_(count) _Post_maybez_ T1* dst,
657 _In_reads_or_z_opt_(count) const T2* src, _In_ size_t count)
658 {
659 assert(dst && src || !count);
660 for (size_t i = 0, j = stdex::strlen<T1>(dst); ; ++i, ++j) {
661 if (i >= count)
662 return j;
663 if ((dst[j] = src[i]) == 0)
664 return j;
665 }
666 }
667
678 template <class T1, class T2>
679 inline size_t strncat(
680 _Out_writes_(count_dst) _Post_maybez_ T1* dst, _In_ size_t count_dst,
681 _In_reads_or_z_opt_(count_src) const T2* src, _In_ size_t count_src)
682 {
683 assert(dst || !count_dst);
684 assert(src || !count_src);
685 for (size_t i = 0, j = stdex::strnlen<T1>(dst, count_dst); ; ++i, ++j)
686 {
687 if (j > count_dst)
688 return j;
689 if (i > count_src) {
690 dst[j] = 0;
691 return j;
692 }
693 if ((dst[j] = src[i]) == 0)
694 return j;
695 }
696 }
697
708 template <class T>
709 inline _Check_return_ _Ret_maybenull_z_ T* strdup(_In_opt_z_ const T* str)
710 {
711 if (!str) _Unlikely_
712 return nullptr;
713 size_t count = strlen(str) + 1;
714 T* dst = new T[count];
715 strncpy(dst, count, str, SIZE_MAX);
716 return dst;
717 }
718
730 template <class T>
731 inline _Ret_z_ T* strndup(
732 _In_reads_or_z_opt_(count) const T* str,
733 _In_ size_t count)
734 {
735 T* dst = new T[count];
736 strncpy(dst, count, str, SIZE_MAX);
737 return dst;
738 }
739
749 template <class T>
750 inline size_t crlf2nl(_Out_writes_z_(strlen(src)) T* dst, _In_z_ const T* src)
751 {
752 assert(dst);
753 assert(src);
754 size_t i, j;
755 for (i = j = 0; src[j];) {
756 if (src[j] != '\r' || src[j + 1] != '\n')
757 dst[i++] = src[j++];
758 else {
759 dst[i++] = '\n';
760 j += 2;
761 }
762 }
763 dst[i] = 0;
764 return i;
765 }
766
768 template <class T, class T_bin>
769 inline T_bin strtoint(
770 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
771 _Out_opt_ size_t* end,
772 _In_ int radix,
773 _Out_ uint8_t& flags)
774 {
775 assert(str || !count);
776 assert(radix == 0 || 2 <= radix && radix <= 36);
777
778 size_t i = 0;
779 T_bin value = 0, digit,
780 max_ui = (T_bin)-1,
781 max_ui_pre1, max_ui_pre2;
782
783 flags = 0;
784
785 // Skip leading spaces.
786 for (;; ++i) {
787 if (i >= count || !str[i]) goto error;
788 if (!isspace(str[i])) break;
789 }
790
791 // Read the sign.
792 if (str[i] == '+') {
793 flags &= ~0x01;
794 ++i;
795 if (i >= count || !str[i]) goto error;
796 }
797 else if (str[i] == '-') {
798 flags |= 0x01;
799 ++i;
800 if (i >= count || !str[i]) goto error;
801 }
802
803 if (radix == 16) {
804 // On hexadecimal, allow leading 0x.
805 if (str[i] == '0' && i + 1 < count && (str[i + 1] == 'x' || str[i + 1] == 'X')) {
806 i += 2;
807 if (i >= count || !str[i]) goto error;
808 }
809 }
810 else if (!radix) {
811 // Autodetect radix.
812 if (str[i] == '0') {
813 ++i;
814 if (i >= count || !str[i]) goto error;
815 if (str[i] == 'x' || str[i] == 'X') {
816 radix = 16;
817 ++i;
818 if (i >= count || !str[i]) goto error;
819 }
820 else
821 radix = 8;
822 }
823 else
824 radix = 10;
825 }
826
827 // We have the radix.
828 max_ui_pre1 = max_ui / (T_bin)radix;
829 max_ui_pre2 = max_ui % (T_bin)radix;
830 for (;;) {
831 if ('0' <= str[i] && str[i] <= '9')
832 digit = (T_bin)str[i] - '0';
833 else if ('A' <= str[i] && str[i] <= 'Z')
834 digit = (T_bin)str[i] - 'A' + '\x0a';
835 else if ('a' <= str[i] && str[i] <= 'z')
836 digit = (T_bin)str[i] - 'a' + '\x0a';
837 else
838 goto error;
839 if (digit >= (T_bin)radix)
840 goto error;
841
842 if (value < max_ui_pre1 || // Multiplication nor addition will not overflow.
843 (value == max_ui_pre1 && digit <= max_ui_pre2)) // Small digits will not overflow.
844 value = value * (T_bin)radix + digit;
845 else {
846 // Overflow!
847 flags |= 0x02;
848 }
849
850 ++i;
851 if (i >= count || !str[i])
852 goto error;
853 }
854
855 error:
856 if (end) *end = i;
857 return value;
858 }
860
871 template <class T, class T_bin>
872 T_bin strtoint(
873 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
874 _Out_opt_ size_t* end,
875 _In_ int radix)
876 {
877 uint8_t flags;
878 T_bin value;
879
880 switch (sizeof(T_bin)) {
881 case 1:
882 value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags);
883 if ((flags & 0x01) && (value & 0x80)) {
884 // Sign bit is 1 => overflow.
885 flags |= 0x02;
886 }
887 return (flags & 0x02) ?
888 (flags & 0x01) ? (T_bin)0x80 : (T_bin)0x7f :
889 (flags & 0x01) ? -value : value;
890
891 case 2:
892 value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags);
893 if ((flags & 0x01) && (value & 0x8000)) {
894 // Sign bit is 1 => overflow.
895 flags |= 0x02;
896 }
897 return (flags & 0x02) ?
898 (flags & 0x01) ? (T_bin)0x8000 : (T_bin)0x7fff :
899 (flags & 0x01) ? -value : value;
900
901 case 4:
902 value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags);
903 if ((flags & 0x01) && (value & 0x80000000)) {
904 // Sign bit is 1 => overflow.
905 flags |= 0x02;
906 }
907 return (flags & 0x02) ?
908 (flags & 0x01) ? (T_bin)0x80000000 : (T_bin)0x7fffffff :
909 (flags & 0x01) ? -value : value;
910
911 case 8:
912 value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags);
913 if ((flags & 0x01) && (value & 0x8000000000000000)) {
914 // Sign bit is 1 => overflow.
915 flags |= 0x02;
916 }
917 return (flags & 0x02) ?
918 (flags & 0x01) ? (T_bin)0x8000000000000000 : (T_bin)0x7fffffffffffffff :
919 (flags & 0x01) ? -value : value;
920
921 default:
922 throw std::invalid_argument("Unsupported bit length");
923 }
924 }
925
936 template <class T, class T_bin>
937 inline T_bin strtouint(
938 _In_reads_or_z_opt_(count) const T* str,
939 _In_ size_t count,
940 _Out_opt_ size_t* end,
941 _In_ int radix)
942 {
943 uint8_t flags;
944 T_bin value;
945
946 switch (sizeof(T_bin)) {
947 case 1: value = (T_bin)strtoint<T, uint8_t>(str, count, end, radix, flags); break;
948 case 2: value = (T_bin)strtoint<T, uint16_t>(str, count, end, radix, flags); break;
949 case 4: value = (T_bin)strtoint<T, uint32_t>(str, count, end, radix, flags); break;
950 case 8: value = (T_bin)strtoint<T, uint64_t>(str, count, end, radix, flags); break;
951 default: throw std::invalid_argument("Unsupported bit length");
952 }
953
954 return (flags & 0x02) ?
955 (flags & 0x01) ? (T_bin)0 : (T_bin)-1 :
956 (flags & 0x01) ? ~value : value;
957 }
958
969 template <class T>
970 inline int32_t strto32(
971 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
972 _Out_opt_ size_t* end,
973 _In_ int radix)
974 {
975 return strtoint<T, int32_t>(str, count, end, radix);
976 }
977
988 template <class T>
989 inline int64_t strto64(
990 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
991 _Out_opt_ size_t* end,
992 _In_ int radix)
993 {
994 return strtoint<T, int64_t>(str, count, end, radix);
995 }
996
1008 template <class T>
1009 inline intptr_t strtoi(
1010 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1011 _Out_opt_ size_t* end,
1012 _In_ int radix)
1013 {
1014#if defined(_WIN64) || defined(__LP64__)
1015 return (intptr_t)strto64(str, count, end, radix);
1016#else
1017 return (intptr_t)strto32(str, count, end, radix);
1018#endif
1019 }
1020
1031 template <class T>
1032 inline uint32_t strtou32(
1033 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1034 _Out_opt_ size_t* end,
1035 _In_ int radix)
1036 {
1037 return strtouint<T, uint32_t>(str, count, end, radix);
1038 }
1039
1050 template <class T>
1051 inline uint64_t strtou64(
1052 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1053 _Out_opt_ size_t* end,
1054 _In_ int radix)
1055 {
1056 return strtouint<T, uint64_t>(str, count, end, radix);
1057 }
1058
1070 template <class T>
1071 inline size_t strtoui(
1072 _In_reads_or_z_opt_(count) const T* str, _In_ size_t count,
1073 _Out_opt_ size_t* end,
1074 _In_ int radix)
1075 {
1076#if defined(_WIN64) || defined(__LP64__)
1077 return (size_t)strtou64(str, count, end, radix);
1078#else
1079 return (size_t)strtou32(str, count, end, radix);
1080#endif
1081 }
1082
1084 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)
1085 {
1086 int r;
1087#ifdef _WIN32
1088 // Don't use _vsnprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1089#pragma warning(suppress: 4996)
1090 r = _vsnprintf_l(str, capacity, format, locale, arg);
1091#else
1092 r = ::vsnprintf(str, capacity, format, arg);
1093#endif
1094 if (r == -1 && strnlen(str, capacity) == capacity) {
1095 // Buffer overrun. Estimate buffer size for the next iteration.
1096 capacity += std::max<size_t>(capacity / 8, 0x80);
1097 if (capacity > INT_MAX)
1098 throw std::invalid_argument("string too big");
1099 return (int)capacity;
1100 }
1101 return r;
1102 }
1103
1104 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)
1105 {
1106 int r;
1107
1108#ifdef _WIN32
1109 // Don't use _vsnwprintf_s(). It terminates the string even if we want to print to the edge of the buffer.
1110#pragma warning(suppress: 4996)
1111 r = _vsnwprintf_l(str, capacity, format, locale, arg);
1112#else
1113 r = vswprintf(str, capacity, format, arg);
1114#endif
1115 if (r == -1 && strnlen(str, capacity) == capacity) {
1116 // Buffer overrun. Estimate buffer size for the next iteration.
1117 capacity += std::max<size_t>(capacity / 8, 0x80);
1118 if (capacity > INT_MAX)
1119 throw std::invalid_argument("string too big");
1120 return (int)capacity;
1121 }
1122 return r;
1123 }
1125
1134 template<class _Elem, class _Traits, class _Ax>
1135 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)
1136 {
1137 _Elem buf[1024/sizeof(_Elem)];
1138
1139 // Try with stack buffer first.
1140 int count = vsnprintf(buf, _countof(buf) - 1, format, locale, arg);
1141 if (count >= 0) {
1142 // Copy from stack.
1143 str.append(buf, count);
1144 } else {
1145 for (size_t capacity = 2*1024/sizeof(_Elem);; capacity *= 2) {
1146 // Allocate on heap and retry.
1147 auto buf_dyn = std::make_unique<_Elem[]>(capacity);
1148 count = vsnprintf(buf_dyn.get(), capacity - 1, format, locale, arg);
1149 if (count >= 0) {
1150 str.append(buf_dyn.get(), count);
1151 break;
1152 }
1153 }
1154 }
1155 }
1156
1164 template<class _Elem, class _Traits, class _Ax>
1165 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, ...)
1166 {
1167 va_list arg;
1168 va_start(arg, locale);
1169 vappendf(str, format, locale, arg);
1170 va_end(arg);
1171 }
1172
1181 template<class _Elem, class _Traits, class _Ax>
1182 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)
1183 {
1184 str.clear();
1185 vappendf(str, format, locale, arg);
1186 }
1187
1195 template<class _Elem, class _Traits, class _Ax>
1196 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, ...)
1197 {
1198 va_list arg;
1199 va_start(arg, locale);
1200 vsprintf(str, format, locale, arg);
1201 va_end(arg);
1202 }
1203
1213 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1214 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)
1215 {
1216 std::basic_string<_Elem, _Traits, _Ax> str;
1217 vappendf(str, format, locale, arg);
1218 return str;
1219 }
1220
1229 template<class _Elem, class _Traits = std::char_traits<_Elem>, class _Ax = std::allocator<_Elem>>
1230 inline std::basic_string<_Elem, _Traits, _Ax> sprintf(_In_z_ _Printf_format_string_params_(2) const _Elem *format, _In_opt_ locale_t locale, ...)
1231 {
1232 va_list arg;
1233 va_start(arg, locale);
1234 auto str = vsprintf(format, locale, arg);
1235 va_end(arg);
1236 return str;
1237 }
1238}
Deleter for unique_ptr using free_locale.
Definition string.hpp:56
void operator()(locale_t locale) const
Delete a pointer.
Definition string.hpp:60