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