File: snprintf.hpp

package info (click to toggle)
boost1.90 1.90.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 593,168 kB
  • sloc: cpp: 4,190,642; xml: 196,648; python: 34,618; ansic: 23,145; asm: 5,468; sh: 3,776; makefile: 1,162; perl: 1,020; sql: 728; ruby: 676; yacc: 478; java: 77; lisp: 24; csh: 6
file content (173 lines) | stat: -rw-r--r-- 4,801 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/*
 *             Copyright Andrey Semashev 2022.
 * Distributed under the Boost Software License, Version 1.0.
 *    (See accompanying file LICENSE_1_0.txt or copy at
 *          http://www.boost.org/LICENSE_1_0.txt)
 */
/*!
 * \file   snprintf.hpp
 * \author Andrey Semashev
 * \date   06.12.2022
 *
 * \brief  The header provides more portable definition of snprintf and vsnprintf,
 *         as well as \c wchar_t counterparts.
 */

#ifndef BOOST_CORE_SNPRINTF_HPP_INCLUDED_
#define BOOST_CORE_SNPRINTF_HPP_INCLUDED_

#include <stdio.h>
#include <wchar.h>
#include <boost/config.hpp>

#ifdef BOOST_HAS_PRAGMA_ONCE
#pragma once
#endif

#if defined(__MINGW32__)

#include <cstddef>
#include <cstdarg>
#if !defined(__MINGW64_VERSION_MAJOR)
#include <climits>
#endif

// MinGW32 and MinGW-w64 provide their own snprintf implementations that are compliant with the C standard.
#define BOOST_CORE_DETAIL_MINGW_SNPRINTF

#elif (defined(BOOST_MSSTL_VERSION) && BOOST_MSSTL_VERSION < 140)

#include <cstddef>
#include <cstdarg>
#include <climits>

// MSVC snprintfs are not conforming but they are good enough for typical use cases.
#define BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF

#endif

namespace boost {

namespace core {

#if defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF) || defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)

#if defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF)

inline int vsnprintf(char* buf, std::size_t size, const char* format, std::va_list args)
{
    return __mingw_vsnprintf(buf, size, format, args);
}

inline int vswprintf(wchar_t* buf, std::size_t size, const wchar_t* format, std::va_list args)
{
#if defined(__MINGW64_VERSION_MAJOR)
    int res = __mingw_vsnwprintf(buf, size, format, args);
    // __mingw_vsnwprintf returns the number of characters to be printed, but (v)swprintf is expected to return -1 on truncation
    if (static_cast< unsigned int >(res) >= size)
        res = -1;
    return res;
#else
    // Legacy MinGW32 does not provide __mingw_vsnwprintf, so use _vsnwprintf from MSVC CRT
    if (BOOST_UNLIKELY(size == 0u || size > static_cast< std::size_t >(INT_MAX)))
        return -1;

    int res = _vsnwprintf(buf, size, format, args);
    // (v)swprintf is expected to return -1 on truncation, so we only need to ensure the output is null-terminated
    if (static_cast< unsigned int >(res) >= size)
    {
        buf[size - 1u] = L'\0';
        res = -1;
    }

    return res;
#endif
}

#elif defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)

#if defined(_MSC_VER)
#pragma warning(push)
// '_vsnprintf': This function or variable may be unsafe. Consider using _vsnprintf_s instead.
#pragma warning(disable: 4996)
#endif

inline int vsnprintf(char* buf, std::size_t size, const char* format, std::va_list args)
{
    if (BOOST_UNLIKELY(size == 0u))
        return 0;
    if (BOOST_UNLIKELY(size > static_cast< std::size_t >(INT_MAX)))
        return -1;

    buf[size - 1u] = '\0';
    int res = _vsnprintf(buf, size, format, args);
    if (static_cast< unsigned int >(res) >= size)
    {
        // _vsnprintf returns -1 if the output was truncated and in case of other errors.
        // Detect truncation by checking whether the output buffer was written over entirely.
        if (buf[size - 1u] != '\0')
        {
            buf[size - 1u] = '\0';
            res = static_cast< int >(size);
        }
    }

    return res;
}

inline int vswprintf(wchar_t* buf, std::size_t size, const wchar_t* format, std::va_list args)
{
    if (BOOST_UNLIKELY(size == 0u || size > static_cast< std::size_t >(INT_MAX)))
        return -1;

    int res = _vsnwprintf(buf, size, format, args);
    // (v)swprintf is expected to return -1 on truncation, so we only need to ensure the output is null-terminated
    if (static_cast< unsigned int >(res) >= size)
    {
        buf[size - 1u] = L'\0';
        res = -1;
    }

    return res;
}

#if defined(_MSC_VER)
#pragma warning(pop)
#endif

#endif

inline int snprintf(char* buf, std::size_t size, const char* format, ...)
{
    std::va_list args;
    va_start(args, format);
    int res = vsnprintf(buf, size, format, args);
    va_end(args);
    return res;
}

inline int swprintf(wchar_t* buf, std::size_t size, const wchar_t* format, ...)
{
    std::va_list args;
    va_start(args, format);
    int res = vswprintf(buf, size, format, args);
    va_end(args);
    return res;
}

#else // defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF) || defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)

// Standard-conforming compilers already have the correct snprintfs
using ::snprintf;
using ::vsnprintf;

using ::swprintf;
using ::vswprintf;

#endif // defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF) || defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)

} // namespace core

} // namespace boost

#endif // BOOST_CORE_SNPRINTF_HPP_INCLUDED_