FdPrintf.cpp (5475B)
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 #include <cstdarg> 8 9 #ifdef _WIN32 10 # include <windows.h> 11 #else 12 # include <unistd.h> 13 #endif 14 #include <cmath> 15 #include <cstring> 16 #include "mozilla/Assertions.h" 17 #include "FdPrintf.h" 18 19 /* Template class allowing a limited number of increments on a value */ 20 template <typename T> 21 class CheckedIncrement { 22 public: 23 CheckedIncrement(T aValue, size_t aMaxIncrement) 24 : mValue(aValue), mMaxIncrement(aMaxIncrement) {} 25 26 T operator++(int) { 27 if (!mMaxIncrement) { 28 MOZ_CRASH("overflow detected"); 29 } 30 mMaxIncrement--; 31 return mValue++; 32 } 33 34 T& operator++() { 35 (*this)++; 36 return mValue; 37 } 38 39 void advance(T end) { 40 // Only makes sense if T is a pointer type. 41 size_t diff = end - mValue; 42 if (diff > mMaxIncrement) { 43 MOZ_CRASH("overflow detected"); 44 } 45 mMaxIncrement -= diff; 46 mValue = end; 47 } 48 49 void rewind(T pos) { 50 size_t diff = mValue - pos; 51 mMaxIncrement += diff; 52 mValue = pos; 53 } 54 55 operator T() { return mValue; } 56 T value() { return mValue; } 57 58 private: 59 T mValue; 60 size_t mMaxIncrement; 61 }; 62 63 template <typename T> 64 static unsigned NumDigits(T n) { 65 if (n < 1) { 66 // We want one digit, it will be 0. 67 return 1; 68 } 69 70 double l = log10(static_cast<double>(n)); 71 double cl = ceil(l); 72 return l == cl ? unsigned(cl) + 1 : unsigned(cl); 73 } 74 75 static void LeftPad(CheckedIncrement<char*>& b, size_t pad) { 76 while (pad-- > 0) { 77 *(b++) = ' '; 78 } 79 } 80 81 // Write the digits into the buffer. 82 static void WriteDigits(CheckedIncrement<char*>& b, size_t i, 83 size_t num_digits) { 84 size_t x = pow(10, double(num_digits - 1)); 85 do { 86 *(b++) = "0123456789"[(i / x) % 10]; 87 x /= 10; 88 } while (x > 0); 89 } 90 91 int VSNPrintf(char* aBuf, size_t aSize, const char* aFormat, va_list aArgs) { 92 CheckedIncrement<char*> b(aBuf, aSize); 93 CheckedIncrement<const char*> f(aFormat, strlen(aFormat) + 1); 94 while (true) { 95 switch (*f) { 96 case '\0': 97 return b - aBuf; 98 99 case '%': { 100 // The start of the format specifier is used if this specifier is 101 // invalid. 102 const char* start = f; 103 104 // Read the field width 105 f++; 106 char* end = nullptr; 107 size_t width = strtoul(f, &end, 10); 108 // If strtol can't find a number that's okay, that means 0 in our 109 // case, but we must advance f). 110 f.advance(end); 111 112 switch (*f) { 113 case 'z': { 114 if (*(++f) == 'u') { 115 size_t i = va_arg(aArgs, size_t); 116 117 size_t num_digits = NumDigits(i); 118 LeftPad(b, width > num_digits ? width - num_digits : 0); 119 WriteDigits(b, i, num_digits); 120 } else { 121 // If the format specifier is unknown then write out '%' and 122 // rewind to the beginning of the specifier causing it to be 123 // printed normally. 124 *(b++) = '%'; 125 f.rewind(start); 126 } 127 break; 128 } 129 130 case 'p': { 131 intptr_t ptr = va_arg(aArgs, intptr_t); 132 *(b++) = '0'; 133 *(b++) = 'x'; 134 int x = sizeof(intptr_t) * 8; 135 bool wrote_msb = false; 136 do { 137 x -= 4; 138 size_t hex_digit = ptr >> x & 0xf; 139 if (hex_digit || wrote_msb) { 140 *(b++) = "0123456789abcdef"[hex_digit]; 141 wrote_msb = true; 142 } 143 } while (x > 0); 144 if (!wrote_msb) { 145 *(b++) = '0'; 146 } 147 break; 148 } 149 150 case 's': { 151 const char* str = va_arg(aArgs, const char*); 152 size_t len = strlen(str); 153 154 LeftPad(b, width > len ? width - len : 0); 155 156 while (*str) { 157 *(b++) = *(str++); 158 } 159 160 break; 161 } 162 163 case '%': 164 // Print a single raw '%'. 165 *(b++) = '%'; 166 break; 167 168 default: 169 // If the format specifier is unknown then write out '%' and 170 // rewind to the beginning of the specifier causing it to be 171 // printed normally. 172 *(b++) = '%'; 173 f.rewind(start); 174 break; 175 } 176 break; 177 } 178 default: 179 *(b++) = *f; 180 break; 181 } 182 f++; 183 } 184 } 185 186 int SNPrintf(char* aBuf, size_t aSize, const char* aFormat, ...) { 187 va_list args; 188 va_start(args, aFormat); 189 int ret = VSNPrintf(aBuf, aSize, aFormat, args); 190 va_end(args); 191 return ret; 192 } 193 194 void VFdPrintf(platform_handle_t aFd, const char* aFormat, va_list aArgs) { 195 char buf[256]; 196 int len = VSNPrintf(buf, 256, aFormat, aArgs); 197 FdPuts(aFd, buf, len); 198 } 199 200 void FdPrintf(platform_handle_t aFd, const char* aFormat, ...) { 201 va_list args; 202 va_start(args, aFormat); 203 VFdPrintf(aFd, aFormat, args); 204 va_end(args); 205 } 206 207 void FdPuts(platform_handle_t aFd, const char* aBuf, size_t aSize) { 208 if (aFd == 0) { 209 return; 210 } 211 212 #ifdef _WIN32 213 // See comment in FdPrintf.h as to why WriteFile is used. 214 DWORD written; 215 WriteFile(aFd, aBuf, aSize, &written, nullptr); 216 #else 217 [[maybe_unused]] ssize_t _ = write(aFd, aBuf, aSize); 218 #endif 219 }