Printf.h (8849B)
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 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. */ 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 { 128 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: 149 explicit SprintfState(char* base) 150 : mMaxlen(base ? strlen(base) : 0), 151 mBase(base), 152 mCur(base ? base + mMaxlen : 0) {} 153 154 ~SprintfState() { this->free_(mBase); } 155 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 162 SmprintfPolicyPointer<AllocPolicy> release() { 163 SmprintfPolicyPointer<AllocPolicy> result(mBase); 164 mBase = nullptr; 165 return result; 166 } 167 168 protected: 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) 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) 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) 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) 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 */