1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
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* fmt, va_list) MOZ_FORMAT_PRINTF(2, 0);
78 
79   /* Fast paths for formatting integers as though by %d, %o, %u, or %x.
80      Since octal and hex formatting always treat numbers as unsigned, there
81      are no signed overloads for AppendInt{Oct,Hex}.  */
82   bool MFBT_API appendIntDec(int32_t);
83   bool MFBT_API appendIntDec(uint32_t);
84   bool MFBT_API appendIntOct(uint32_t);
85   bool MFBT_API appendIntHex(uint32_t);
86   bool MFBT_API appendIntDec(int64_t);
87   bool MFBT_API appendIntDec(uint64_t);
88   bool MFBT_API appendIntOct(uint64_t);
89   bool MFBT_API appendIntHex(uint64_t);
90 
emitted()91   inline size_t emitted() { return mEmitted; }
92 
93  protected:
94   MFBT_API PrintfTarget();
95   virtual ~PrintfTarget() = default;
96 
97   /* Subclasses override this.  It is called when more output is
98      available.  It may be called with len==0.  This should return
99      true on success, or false on failure.  */
100   virtual bool append(const char* sp, size_t len) = 0;
101 
102  private:
103   /* Number of bytes emitted so far.  */
104   size_t mEmitted;
105 
106   /* The implementation calls this to emit bytes and update
107      mEmitted.  */
emit(const char * sp,size_t len)108   bool emit(const char* sp, size_t len) {
109     mEmitted += len;
110     return append(sp, len);
111   }
112 
113   bool fill2(const char* src, int srclen, int width, int flags);
114   bool fill_n(const char* src, int srclen, int width, int prec, int type,
115               int flags);
116   bool cvt_l(long num, int width, int prec, int radix, int type, int flags,
117              const char* hexp);
118   bool cvt_ll(int64_t num, int width, int prec, int radix, int type, int flags,
119               const char* hexp);
120   bool cvt_f(double d, char c, int width, int prec, int flags);
121   bool cvt_s(const char* s, int width, int prec, int flags);
122 };
123 
124 namespace detail {
125 
126 template <typename AllocPolicy = mozilla::MallocAllocPolicy>
127 struct AllocPolicyBasedFreePolicy {
operatorAllocPolicyBasedFreePolicy128   void operator()(const void* ptr) {
129     AllocPolicy policy;
130     policy.free_(const_cast<void*>(ptr));
131   }
132 };
133 
134 }  // namespace detail
135 
136 // The type returned by Smprintf and friends.
137 template <typename AllocPolicy>
138 using SmprintfPolicyPointer =
139     mozilla::UniquePtr<char, detail::AllocPolicyBasedFreePolicy<AllocPolicy>>;
140 
141 // The default type if no alloc policy is specified.
142 typedef SmprintfPolicyPointer<mozilla::MallocAllocPolicy> SmprintfPointer;
143 
144 // Used in the implementation of Smprintf et al.
145 template <typename AllocPolicy>
146 class MOZ_STACK_CLASS SprintfState final : private mozilla::PrintfTarget,
147                                            private AllocPolicy {
148  public:
SprintfState(char * base)149   explicit SprintfState(char* base)
150       : mMaxlen(base ? strlen(base) : 0),
151         mBase(base),
152         mCur(base ? base + mMaxlen : 0) {}
153 
~SprintfState()154   ~SprintfState() { this->free_(mBase); }
155 
vprint(const char * format,va_list ap_list)156   bool vprint(const char* format, va_list ap_list) MOZ_FORMAT_PRINTF(2, 0) {
157     // The "" here has a single \0 character, which is what we're
158     // trying to append.
159     return mozilla::PrintfTarget::vprint(format, ap_list) && append("", 1);
160   }
161 
release()162   SmprintfPolicyPointer<AllocPolicy> release() {
163     SmprintfPolicyPointer<AllocPolicy> result(mBase);
164     mBase = nullptr;
165     return result;
166   }
167 
168  protected:
append(const char * sp,size_t len)169   bool append(const char* sp, size_t len) override {
170     ptrdiff_t off;
171     char* newbase;
172     size_t newlen;
173 
174     off = mCur - mBase;
175     if (off + len >= mMaxlen) {
176       /* Grow the buffer */
177       newlen = mMaxlen + ((len > 32) ? len : 32);
178       newbase = this->template maybe_pod_malloc<char>(newlen);
179       if (!newbase) {
180         /* Ran out of memory */
181         return false;
182       }
183       memcpy(newbase, mBase, mMaxlen);
184       this->free_(mBase);
185       mBase = newbase;
186       mMaxlen = newlen;
187       mCur = mBase + off;
188     }
189 
190     /* Copy data */
191     memcpy(mCur, sp, len);
192     mCur += len;
193     MOZ_ASSERT(size_t(mCur - mBase) <= mMaxlen);
194     return true;
195   }
196 
197  private:
198   size_t mMaxlen;
199   char* mBase;
200   char* mCur;
201 };
202 
203 /*
204 ** sprintf into a malloc'd buffer. Return a pointer to the malloc'd
205 ** buffer on success, nullptr on failure. Call AllocPolicy::free_ to release
206 ** the memory returned.
207 */
208 template <typename AllocPolicy = mozilla::MallocAllocPolicy>
209 MOZ_FORMAT_PRINTF(1, 2)
Smprintf(const char * fmt,...)210 SmprintfPolicyPointer<AllocPolicy> Smprintf(const char* fmt, ...) {
211   SprintfState<AllocPolicy> ss(nullptr);
212   va_list ap;
213   va_start(ap, fmt);
214   bool r = ss.vprint(fmt, ap);
215   va_end(ap);
216   if (!r) {
217     return nullptr;
218   }
219   return ss.release();
220 }
221 
222 /*
223 ** "append" sprintf into a malloc'd buffer. "last" is the last value of
224 ** the malloc'd buffer. sprintf will append data to the end of last,
225 ** growing it as necessary using realloc. If last is nullptr, SmprintfAppend
226 ** will allocate the initial string. The return value is the new value of
227 ** last for subsequent calls, or nullptr if there is a malloc failure.
228 */
229 template <typename AllocPolicy = mozilla::MallocAllocPolicy>
230 MOZ_FORMAT_PRINTF(2, 3)
SmprintfAppend(SmprintfPolicyPointer<AllocPolicy> && last,const char * fmt,...)231 SmprintfPolicyPointer<AllocPolicy> SmprintfAppend(
232     SmprintfPolicyPointer<AllocPolicy>&& last, const char* fmt, ...) {
233   SprintfState<AllocPolicy> ss(last.release());
234   va_list ap;
235   va_start(ap, fmt);
236   bool r = ss.vprint(fmt, ap);
237   va_end(ap);
238   if (!r) {
239     return nullptr;
240   }
241   return ss.release();
242 }
243 
244 /*
245 ** va_list forms of the above.
246 */
247 template <typename AllocPolicy = mozilla::MallocAllocPolicy>
248 MOZ_FORMAT_PRINTF(1, 0)
Vsmprintf(const char * fmt,va_list ap)249 SmprintfPolicyPointer<AllocPolicy> Vsmprintf(const char* fmt, va_list ap) {
250   SprintfState<AllocPolicy> ss(nullptr);
251   if (!ss.vprint(fmt, ap)) return nullptr;
252   return ss.release();
253 }
254 
255 template <typename AllocPolicy = mozilla::MallocAllocPolicy>
256 MOZ_FORMAT_PRINTF(2, 0)
VsmprintfAppend(SmprintfPolicyPointer<AllocPolicy> && last,const char * fmt,va_list ap)257 SmprintfPolicyPointer<AllocPolicy> VsmprintfAppend(
258     SmprintfPolicyPointer<AllocPolicy>&& last, const char* fmt, va_list ap) {
259   SprintfState<AllocPolicy> ss(last.release());
260   if (!ss.vprint(fmt, ap)) return nullptr;
261   return ss.release();
262 }
263 
264 }  // namespace mozilla
265 
266 #endif /* mozilla_Printf_h */
267