snprintf.hpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. /*
  2. * Copyright Andrey Semashev 2022.
  3. * Distributed under the Boost Software License, Version 1.0.
  4. * (See accompanying file LICENSE_1_0.txt or copy at
  5. * http://www.boost.org/LICENSE_1_0.txt)
  6. */
  7. /*!
  8. * \file snprintf.hpp
  9. * \author Andrey Semashev
  10. * \date 06.12.2022
  11. *
  12. * \brief The header provides more portable definition of snprintf and vsnprintf,
  13. * as well as \c wchar_t counterparts.
  14. */
  15. #ifndef BOOST_CORE_SNPRINTF_HPP_INCLUDED_
  16. #define BOOST_CORE_SNPRINTF_HPP_INCLUDED_
  17. #include <stdio.h>
  18. #include <wchar.h>
  19. #include <boost/config.hpp>
  20. #ifdef BOOST_HAS_PRAGMA_ONCE
  21. #pragma once
  22. #endif
  23. #if defined(__MINGW32__)
  24. #include <cstddef>
  25. #include <cstdarg>
  26. #if !defined(__MINGW64_VERSION_MAJOR)
  27. #include <climits>
  28. #endif
  29. // MinGW32 and MinGW-w64 provide their own snprintf implementations that are compliant with the C standard.
  30. #define BOOST_CORE_DETAIL_MINGW_SNPRINTF
  31. #elif (defined(BOOST_MSSTL_VERSION) && BOOST_MSSTL_VERSION < 140)
  32. #include <cstddef>
  33. #include <cstdarg>
  34. #include <climits>
  35. // MSVC snprintfs are not conforming but they are good enough for typical use cases.
  36. #define BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF
  37. #endif
  38. namespace boost {
  39. namespace core {
  40. #if defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF) || defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)
  41. #if defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF)
  42. inline int vsnprintf(char* buf, std::size_t size, const char* format, std::va_list args)
  43. {
  44. return __mingw_vsnprintf(buf, size, format, args);
  45. }
  46. inline int vswprintf(wchar_t* buf, std::size_t size, const wchar_t* format, std::va_list args)
  47. {
  48. #if defined(__MINGW64_VERSION_MAJOR)
  49. int res = __mingw_vsnwprintf(buf, size, format, args);
  50. // __mingw_vsnwprintf returns the number of characters to be printed, but (v)swprintf is expected to return -1 on truncation
  51. if (static_cast< unsigned int >(res) >= size)
  52. res = -1;
  53. return res;
  54. #else
  55. // Legacy MinGW32 does not provide __mingw_vsnwprintf, so use _vsnwprintf from MSVC CRT
  56. if (BOOST_UNLIKELY(size == 0u || size > static_cast< std::size_t >(INT_MAX)))
  57. return -1;
  58. int res = _vsnwprintf(buf, size, format, args);
  59. // (v)swprintf is expected to return -1 on truncation, so we only need to ensure the output is null-terminated
  60. if (static_cast< unsigned int >(res) >= size)
  61. {
  62. buf[size - 1u] = L'\0';
  63. res = -1;
  64. }
  65. return res;
  66. #endif
  67. }
  68. #elif defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)
  69. #if defined(_MSC_VER)
  70. #pragma warning(push)
  71. // '_vsnprintf': This function or variable may be unsafe. Consider using _vsnprintf_s instead.
  72. #pragma warning(disable: 4996)
  73. #endif
  74. inline int vsnprintf(char* buf, std::size_t size, const char* format, std::va_list args)
  75. {
  76. if (BOOST_UNLIKELY(size == 0u))
  77. return 0;
  78. if (BOOST_UNLIKELY(size > static_cast< std::size_t >(INT_MAX)))
  79. return -1;
  80. buf[size - 1u] = '\0';
  81. int res = _vsnprintf(buf, size, format, args);
  82. if (static_cast< unsigned int >(res) >= size)
  83. {
  84. // _vsnprintf returns -1 if the output was truncated and in case of other errors.
  85. // Detect truncation by checking whether the output buffer was written over entirely.
  86. if (buf[size - 1u] != '\0')
  87. {
  88. buf[size - 1u] = '\0';
  89. res = static_cast< int >(size);
  90. }
  91. }
  92. return res;
  93. }
  94. inline int vswprintf(wchar_t* buf, std::size_t size, const wchar_t* format, std::va_list args)
  95. {
  96. if (BOOST_UNLIKELY(size == 0u || size > static_cast< std::size_t >(INT_MAX)))
  97. return -1;
  98. int res = _vsnwprintf(buf, size, format, args);
  99. // (v)swprintf is expected to return -1 on truncation, so we only need to ensure the output is null-terminated
  100. if (static_cast< unsigned int >(res) >= size)
  101. {
  102. buf[size - 1u] = L'\0';
  103. res = -1;
  104. }
  105. return res;
  106. }
  107. #if defined(_MSC_VER)
  108. #pragma warning(pop)
  109. #endif
  110. #endif
  111. inline int snprintf(char* buf, std::size_t size, const char* format, ...)
  112. {
  113. std::va_list args;
  114. va_start(args, format);
  115. int res = vsnprintf(buf, size, format, args);
  116. va_end(args);
  117. return res;
  118. }
  119. inline int swprintf(wchar_t* buf, std::size_t size, const wchar_t* format, ...)
  120. {
  121. std::va_list args;
  122. va_start(args, format);
  123. int res = vswprintf(buf, size, format, args);
  124. va_end(args);
  125. return res;
  126. }
  127. #else // defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF) || defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)
  128. // Standard-conforming compilers already have the correct snprintfs
  129. using ::snprintf;
  130. using ::vsnprintf;
  131. using ::swprintf;
  132. using ::vswprintf;
  133. #endif // defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF) || defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)
  134. } // namespace core
  135. } // namespace boost
  136. #endif // BOOST_CORE_SNPRINTF_HPP_INCLUDED_