Line data Source code
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 : ** You should use PRI*SIZE macros instead
42 : ** %s - string
43 : ** %S, %ls - wide string, that is wchar_t*
44 : ** %c - character
45 : ** %p - pointer (deals with machine dependent pointer size)
46 : ** %f - float; note that this is actually formatted using the
47 : ** system's native printf, and so the results may vary
48 : ** %g - float; note that this is actually formatted using the
49 : ** system's native printf, and so the results may vary
50 : */
51 :
52 : #include "mozilla/AllocPolicy.h"
53 : #include "mozilla/Assertions.h"
54 : #include "mozilla/Attributes.h"
55 : #include "mozilla/IntegerPrintfMacros.h"
56 : #include "mozilla/SizePrintfMacros.h"
57 : #include "mozilla/Types.h"
58 : #include "mozilla/UniquePtr.h"
59 :
60 : #include <stdarg.h>
61 : #include <string.h>
62 :
63 : namespace mozilla {
64 :
65 : /*
66 : * This class may be subclassed to provide a way to get the output of
67 : * a printf-like call, as the output is generated.
68 : */
69 : class PrintfTarget
70 : {
71 : public:
72 : /* The Printf-like interface. */
73 : bool MFBT_API print(const char* format, ...) MOZ_FORMAT_PRINTF(2, 3);
74 :
75 : /* The Vprintf-like interface. */
76 : bool MFBT_API vprint(const char* format, va_list) MOZ_FORMAT_PRINTF(2, 0);
77 :
78 : protected:
79 : MFBT_API PrintfTarget();
80 9146 : virtual ~PrintfTarget() { }
81 :
82 : /* Subclasses override this. It is called when more output is
83 : available. It may be called with len==0. This should return
84 : true on success, or false on failure. */
85 : virtual bool append(const char* sp, size_t len) = 0;
86 :
87 : private:
88 :
89 : /* Number of bytes emitted so far. */
90 : size_t mEmitted;
91 :
92 : /* The implementation calls this to emit bytes and update
93 : mEmitted. */
94 63730 : bool emit(const char* sp, size_t len) {
95 63730 : mEmitted += len;
96 63730 : 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, int flags);
101 : bool cvt_l(long num, int width, int prec, int radix, int type, int flags, const char* hxp);
102 : bool cvt_ll(int64_t num, int width, int prec, int radix, int type, int flags, const char* hexp);
103 : bool cvt_f(double d, const char* fmt0, const char* fmt1);
104 : bool cvt_s(const char* s, int width, int prec, int flags);
105 : };
106 :
107 : namespace detail {
108 :
109 : template<typename AllocPolicy = mozilla::MallocAllocPolicy>
110 : struct AllocPolicyBasedFreePolicy
111 : {
112 5 : void operator()(const void* ptr) {
113 : AllocPolicy policy;
114 5 : policy.free_(const_cast<void*>(ptr));
115 5 : }
116 : };
117 :
118 : }
119 :
120 : // The type returned by Smprintf and friends.
121 : template<typename AllocPolicy>
122 : using SmprintfPolicyPointer = mozilla::UniquePtr<char, detail::AllocPolicyBasedFreePolicy<AllocPolicy>>;
123 :
124 : // The default type if no alloc policy is specified.
125 : typedef SmprintfPolicyPointer<mozilla::MallocAllocPolicy> SmprintfPointer;
126 :
127 : // Used in the implementation of Smprintf et al.
128 : template<typename AllocPolicy>
129 : class MOZ_STACK_CLASS SprintfState final : private mozilla::PrintfTarget, private AllocPolicy
130 : {
131 : public:
132 8274 : explicit SprintfState(char* base)
133 : : mMaxlen(base ? strlen(base) : 0)
134 : , mBase(base)
135 8274 : , mCur(base ? base + mMaxlen : 0)
136 : {
137 8273 : }
138 :
139 8274 : ~SprintfState() {
140 8274 : this->free_(mBase);
141 16548 : }
142 :
143 8273 : bool vprint(const char* format, va_list ap_list) MOZ_FORMAT_PRINTF(2, 0) {
144 : // The "" here has a single \0 character, which is what we're
145 : // trying to append.
146 8273 : return mozilla::PrintfTarget::vprint(format, ap_list) && append("", 1);
147 : }
148 :
149 8274 : SmprintfPolicyPointer<AllocPolicy> release() {
150 8274 : SmprintfPolicyPointer<AllocPolicy> result(mBase);
151 8274 : mBase = nullptr;
152 8274 : return result;
153 : }
154 :
155 : protected:
156 :
157 65955 : bool append(const char* sp, size_t len) override {
158 : ptrdiff_t off;
159 : char* newbase;
160 : size_t newlen;
161 :
162 65955 : off = mCur - mBase;
163 65955 : if (off + len >= mMaxlen) {
164 : /* Grow the buffer */
165 14258 : newlen = mMaxlen + ((len > 32) ? len : 32);
166 14258 : newbase = static_cast<char*>(this->maybe_pod_realloc(mBase, mMaxlen, newlen));
167 14258 : if (!newbase) {
168 : /* Ran out of memory */
169 0 : return false;
170 : }
171 14258 : mBase = newbase;
172 14258 : mMaxlen = newlen;
173 14258 : mCur = mBase + off;
174 : }
175 :
176 : /* Copy data */
177 65955 : memcpy(mCur, sp, len);
178 65955 : mCur += len;
179 65955 : MOZ_ASSERT(size_t(mCur - mBase) <= mMaxlen);
180 65955 : return true;
181 : }
182 :
183 : private:
184 :
185 : size_t mMaxlen;
186 : char* mBase;
187 : char* mCur;
188 : };
189 :
190 : /*
191 : ** sprintf into a malloc'd buffer. Return a pointer to the malloc'd
192 : ** buffer on success, nullptr on failure. Call AllocPolicy::free_ to release
193 : ** the memory returned.
194 : */
195 : template<typename AllocPolicy = mozilla::MallocAllocPolicy>
196 : MOZ_FORMAT_PRINTF(1, 2)
197 3 : SmprintfPolicyPointer<AllocPolicy> Smprintf(const char* fmt, ...)
198 : {
199 6 : SprintfState<AllocPolicy> ss(nullptr);
200 : va_list ap;
201 3 : va_start(ap, fmt);
202 3 : bool r = ss.vprint(fmt, ap);
203 3 : va_end(ap);
204 3 : if (!r) {
205 0 : return nullptr;
206 : }
207 3 : return ss.release();
208 : }
209 :
210 : /*
211 : ** "append" sprintf into a malloc'd buffer. "last" is the last value of
212 : ** the malloc'd buffer. sprintf will append data to the end of last,
213 : ** growing it as necessary using realloc. If last is nullptr, SmprintfAppend
214 : ** will allocate the initial string. The return value is the new value of
215 : ** last for subsequent calls, or nullptr if there is a malloc failure.
216 : */
217 : template<typename AllocPolicy = mozilla::MallocAllocPolicy>
218 : MOZ_FORMAT_PRINTF(2, 3)
219 : SmprintfPolicyPointer<AllocPolicy> SmprintfAppend(SmprintfPolicyPointer<AllocPolicy>&& last,
220 : const char* fmt, ...)
221 : {
222 : SprintfState<AllocPolicy> ss(last.release());
223 : va_list ap;
224 : va_start(ap, fmt);
225 : bool r = ss.vprint(fmt, ap);
226 : va_end(ap);
227 : if (!r) {
228 : return nullptr;
229 : }
230 : return ss.release();
231 : }
232 :
233 : /*
234 : ** va_list forms of the above.
235 : */
236 : template<typename AllocPolicy = mozilla::MallocAllocPolicy>
237 : MOZ_FORMAT_PRINTF(1, 0)
238 8264 : SmprintfPolicyPointer<AllocPolicy> Vsmprintf(const char* fmt, va_list ap)
239 : {
240 16528 : SprintfState<AllocPolicy> ss(nullptr);
241 8263 : if (!ss.vprint(fmt, ap))
242 0 : return nullptr;
243 8264 : return ss.release();
244 : }
245 :
246 : template<typename AllocPolicy = mozilla::MallocAllocPolicy>
247 : MOZ_FORMAT_PRINTF(2, 0)
248 7 : SmprintfPolicyPointer<AllocPolicy> VsmprintfAppend(SmprintfPolicyPointer<AllocPolicy>&& last,
249 : const char* fmt, va_list ap)
250 : {
251 14 : SprintfState<AllocPolicy> ss(last.release());
252 7 : if (!ss.vprint(fmt, ap))
253 0 : return nullptr;
254 7 : return ss.release();
255 : }
256 :
257 : /*
258 : ** Free the memory allocated, for the caller, by Smprintf.
259 : */
260 : template<typename AllocPolicy = mozilla::MallocAllocPolicy>
261 3956 : void SmprintfFree(char* mem)
262 : {
263 : AllocPolicy allocator;
264 3956 : allocator.free_(mem);
265 3956 : }
266 :
267 : } // namespace mozilla
268 :
269 : #endif /* mozilla_Printf_h */
|