1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /* Printf-like functions, with canned variants that malloc their result. */
8
9 #ifndef mozilla_Printf_h
10 #define mozilla_Printf_h
11
12 /*
13 ** API for PR printf like routines.
14 **
15 ** These exist partly for historical reasons -- initially they were in
16 ** NSPR, then forked in tree and modified in js/ -- but now the prime
17 ** motivation is both closer control over the exact formatting (with
18 ** one exception, see below) and also the ability to control where
19 ** exactly the generated results are sent.
20 **
21 ** It might seem that this could all be dispensed with in favor of a
22 ** wrapper around |vsnprintf| -- except that this implementation
23 ** guarantees that the %s format will accept a NULL pointer, whereas
24 ** with standard functions this is undefined.
25 **
26 ** This supports the following formats. It implements a subset of the
27 ** standard formats; due to the use of MOZ_FORMAT_PRINTF, it is not
28 ** permissible to extend the standard, aside from relaxing undefined
29 ** behavior.
30 **
31 ** %d - decimal
32 ** %u - unsigned decimal
33 ** %x - unsigned hex
34 ** %X - unsigned uppercase hex
35 ** %o - unsigned octal
36 ** %hd, %hu, %hx, %hX, %ho - "short" versions of above
37 ** %ld, %lu, %lx, %lX, %lo - "long" versions of above
38 ** %lld, %llu, %llx, %llX, %llo - "long long" versions of above
39 ** %zd, %zo, %zu, %zx, %zX - size_t versions of above
40 ** %Id, %Io, %Iu, %Ix, %IX - size_t versions of above (for Windows compat).
41 ** Note that MSVC 2015 and newer supports the z length modifier so
42 ** users should prefer using %z instead of %I. We are supporting %I in
43 ** addition to %z in case third-party code that uses %I gets routed to
44 ** use this printf implementation.
45 ** %s - string
46 ** %S, %ls - wide string, that is wchar_t*
47 ** %c - character
48 ** %p - pointer (deals with machine dependent pointer size)
49 ** %f - float; note that this is actually formatted using the
50 ** system's native printf, and so the results may vary
51 ** %g - float; note that this is actually formatted using the
52 ** system's native printf, and so the results may vary
53 */
54
55 #include "mozilla/AllocPolicy.h"
56 #include "mozilla/Assertions.h"
57 #include "mozilla/Attributes.h"
58 #include "mozilla/IntegerPrintfMacros.h"
59 #include "mozilla/Types.h"
60 #include "mozilla/UniquePtr.h"
61
62 #include <stdarg.h>
63 #include <string.h>
64
65 namespace mozilla {
66
67 /*
68 * This class may be subclassed to provide a way to get the output of
69 * a printf-like call, as the output is generated.
70 */
71 class PrintfTarget {
72 public:
73 /* The Printf-like interface. */
74 bool MFBT_API print(const char* format, ...) MOZ_FORMAT_PRINTF(2, 3);
75
76 /* The Vprintf-like interface. */
77 bool MFBT_API vprint(const char* format, va_list) MOZ_FORMAT_PRINTF(2, 0);
78
79 protected:
80 MFBT_API PrintfTarget();
~PrintfTarget()81 virtual ~PrintfTarget() {}
82
83 /* Subclasses override this. It is called when more output is
84 available. It may be called with len==0. This should return
85 true on success, or false on failure. */
86 virtual bool append(const char* sp, size_t len) = 0;
87
88 private:
89 /* Number of bytes emitted so far. */
90 size_t mEmitted;
91
92 /* The implementation calls this to emit bytes and update
93 mEmitted. */
emit(const char * sp,size_t len)94 bool emit(const char* sp, size_t len) {
95 mEmitted += len;
96 return append(sp, len);
97 }
98
99 bool fill2(const char* src, int srclen, int width, int flags);
100 bool fill_n(const char* src, int srclen, int width, int prec, int type,
101 int flags);
102 bool cvt_l(long num, int width, int prec, int radix, int type, int flags,
103 const char* hxp);
104 bool cvt_ll(int64_t num, int width, int prec, int radix, int type, int flags,
105 const char* hexp);
106 bool cvt_f(double d, const char* fmt0, const char* fmt1);
107 bool cvt_s(const char* s, int width, int prec, int flags);
108 };
109
110 namespace detail {
111
112 template <typename AllocPolicy = mozilla::MallocAllocPolicy>
113 struct AllocPolicyBasedFreePolicy {
operatorAllocPolicyBasedFreePolicy114 void operator()(const void* ptr) {
115 AllocPolicy policy;
116 policy.free_(const_cast<void*>(ptr));
117 }
118 };
119
120 } // namespace detail
121
122 // The type returned by Smprintf and friends.
123 template <typename AllocPolicy>
124 using SmprintfPolicyPointer =
125 mozilla::UniquePtr<char, detail::AllocPolicyBasedFreePolicy<AllocPolicy>>;
126
127 // The default type if no alloc policy is specified.
128 typedef SmprintfPolicyPointer<mozilla::MallocAllocPolicy> SmprintfPointer;
129
130 // Used in the implementation of Smprintf et al.
131 template <typename AllocPolicy>
132 class MOZ_STACK_CLASS SprintfState final : private mozilla::PrintfTarget,
133 private AllocPolicy {
134 public:
SprintfState(char * base)135 explicit SprintfState(char* base)
136 : mMaxlen(base ? strlen(base) : 0),
137 mBase(base),
138 mCur(base ? base + mMaxlen : 0) {}
139
~SprintfState()140 ~SprintfState() { this->free_(mBase); }
141
vprint(const char * format,va_list ap_list)142 bool vprint(const char* format, va_list ap_list) MOZ_FORMAT_PRINTF(2, 0) {
143 // The "" here has a single \0 character, which is what we're
144 // trying to append.
145 return mozilla::PrintfTarget::vprint(format, ap_list) && append("", 1);
146 }
147
release()148 SmprintfPolicyPointer<AllocPolicy> release() {
149 SmprintfPolicyPointer<AllocPolicy> result(mBase);
150 mBase = nullptr;
151 return result;
152 }
153
154 protected:
append(const char * sp,size_t len)155 bool append(const char* sp, size_t len) override {
156 ptrdiff_t off;
157 char* newbase;
158 size_t newlen;
159
160 off = mCur - mBase;
161 if (off + len >= mMaxlen) {
162 /* Grow the buffer */
163 newlen = mMaxlen + ((len > 32) ? len : 32);
164 newbase =
165 static_cast<char*>(this->maybe_pod_realloc(mBase, mMaxlen, newlen));
166 if (!newbase) {
167 /* Ran out of memory */
168 return false;
169 }
170 mBase = newbase;
171 mMaxlen = newlen;
172 mCur = mBase + off;
173 }
174
175 /* Copy data */
176 memcpy(mCur, sp, len);
177 mCur += len;
178 MOZ_ASSERT(size_t(mCur - mBase) <= mMaxlen);
179 return true;
180 }
181
182 private:
183 size_t mMaxlen;
184 char* mBase;
185 char* mCur;
186 };
187
188 /*
189 ** sprintf into a malloc'd buffer. Return a pointer to the malloc'd
190 ** buffer on success, nullptr on failure. Call AllocPolicy::free_ to release
191 ** the memory returned.
192 */
193 template <typename AllocPolicy = mozilla::MallocAllocPolicy>
194 MOZ_FORMAT_PRINTF(1, 2)
Smprintf(const char * fmt,...)195 SmprintfPolicyPointer<AllocPolicy> Smprintf(const char* fmt, ...) {
196 SprintfState<AllocPolicy> ss(nullptr);
197 va_list ap;
198 va_start(ap, fmt);
199 bool r = ss.vprint(fmt, ap);
200 va_end(ap);
201 if (!r) {
202 return nullptr;
203 }
204 return ss.release();
205 }
206
207 /*
208 ** "append" sprintf into a malloc'd buffer. "last" is the last value of
209 ** the malloc'd buffer. sprintf will append data to the end of last,
210 ** growing it as necessary using realloc. If last is nullptr, SmprintfAppend
211 ** will allocate the initial string. The return value is the new value of
212 ** last for subsequent calls, or nullptr if there is a malloc failure.
213 */
214 template <typename AllocPolicy = mozilla::MallocAllocPolicy>
215 MOZ_FORMAT_PRINTF(2, 3)
SmprintfAppend(SmprintfPolicyPointer<AllocPolicy> && last,const char * fmt,...)216 SmprintfPolicyPointer<AllocPolicy> SmprintfAppend(
217 SmprintfPolicyPointer<AllocPolicy>&& last, const char* fmt, ...) {
218 SprintfState<AllocPolicy> ss(last.release());
219 va_list ap;
220 va_start(ap, fmt);
221 bool r = ss.vprint(fmt, ap);
222 va_end(ap);
223 if (!r) {
224 return nullptr;
225 }
226 return ss.release();
227 }
228
229 /*
230 ** va_list forms of the above.
231 */
232 template <typename AllocPolicy = mozilla::MallocAllocPolicy>
233 MOZ_FORMAT_PRINTF(1, 0)
Vsmprintf(const char * fmt,va_list ap)234 SmprintfPolicyPointer<AllocPolicy> Vsmprintf(const char* fmt, va_list ap) {
235 SprintfState<AllocPolicy> ss(nullptr);
236 if (!ss.vprint(fmt, ap)) return nullptr;
237 return ss.release();
238 }
239
240 template <typename AllocPolicy = mozilla::MallocAllocPolicy>
241 MOZ_FORMAT_PRINTF(2, 0)
VsmprintfAppend(SmprintfPolicyPointer<AllocPolicy> && last,const char * fmt,va_list ap)242 SmprintfPolicyPointer<AllocPolicy> VsmprintfAppend(
243 SmprintfPolicyPointer<AllocPolicy>&& last, const char* fmt, va_list ap) {
244 SprintfState<AllocPolicy> ss(last.release());
245 if (!ss.vprint(fmt, ap)) return nullptr;
246 return ss.release();
247 }
248
249 } // namespace mozilla
250
251 #endif /* mozilla_Printf_h */
252