Serenity Operating System
at hosted 447 lines 14 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#pragma once 28 29#include <AK/Assertions.h> 30#include <AK/LogStream.h> 31#include <AK/StdLibExtras.h> 32#include <AK/Types.h> 33#include <stdarg.h> 34 35static constexpr const char* printf_hex_digits_lower = "0123456789abcdef"; 36static constexpr const char* printf_hex_digits_upper = "0123456789ABCDEF"; 37 38#ifdef __serenity__ 39extern "C" size_t strlen(const char*); 40#else 41# include <string.h> 42#endif 43 44template<typename PutChFunc, typename T> 45[[gnu::always_inline]] inline int print_hex(PutChFunc putch, char*& bufptr, T number, bool upper_case, bool alternate_form, bool left_pad, bool zeroPad, u8 width) 46{ 47 int ret = 0; 48 49 int digits = 0; 50 for (T n = number; n > 0; n >>= 4) 51 ++digits; 52 if (digits == 0) 53 digits = 1; 54 55 if (left_pad) { 56 int stop_at = width - digits; 57 if (alternate_form) 58 stop_at -= 2; 59 60 while (ret < stop_at) { 61 putch(bufptr, ' '); 62 ++ret; 63 } 64 } 65 66 if (alternate_form) { 67 putch(bufptr, '0'); 68 putch(bufptr, 'x'); 69 ret += 2; 70 width += 2; 71 } 72 73 if (zeroPad) { 74 while (ret < width - digits) { 75 putch(bufptr, '0'); 76 ++ret; 77 } 78 } 79 80 if (number == 0) { 81 putch(bufptr, '0'); 82 ++ret; 83 } else { 84 u8 shift_count = digits * 4; 85 while (shift_count) { 86 shift_count -= 4; 87 putch(bufptr, 88 upper_case 89 ? printf_hex_digits_upper[(number >> shift_count) & 0x0f] 90 : printf_hex_digits_lower[(number >> shift_count) & 0x0f]); 91 ++ret; 92 } 93 } 94 95 return ret; 96} 97 98template<typename PutChFunc> 99[[gnu::always_inline]] inline int print_number(PutChFunc putch, char*& bufptr, u32 number, bool leftPad, bool zeroPad, u32 fieldWidth) 100{ 101 u32 divisor = 1000000000; 102 char ch; 103 char padding = 1; 104 char buf[16]; 105 char* p = buf; 106 107 for (;;) { 108 ch = '0' + (number / divisor); 109 number %= divisor; 110 if (ch != '0') 111 padding = 0; 112 if (!padding || divisor == 1) 113 *(p++) = ch; 114 if (divisor == 1) 115 break; 116 divisor /= 10; 117 } 118 119 size_t numlen = p - buf; 120 if (!fieldWidth || fieldWidth < numlen) 121 fieldWidth = numlen; 122 if (!leftPad) { 123 for (unsigned i = 0; i < fieldWidth - numlen; ++i) { 124 putch(bufptr, zeroPad ? '0' : ' '); 125 } 126 } 127 for (unsigned i = 0; i < numlen; ++i) { 128 putch(bufptr, buf[i]); 129 } 130 if (leftPad) { 131 for (unsigned i = 0; i < fieldWidth - numlen; ++i) { 132 putch(bufptr, ' '); 133 } 134 } 135 136 return fieldWidth; 137} 138 139template<typename PutChFunc> 140[[gnu::always_inline]] inline int print_u64(PutChFunc putch, char*& bufptr, u64 number, bool leftPad, bool zeroPad, u32 fieldWidth) 141{ 142 u64 divisor = 10000000000000000000LLU; 143 char ch; 144 char padding = 1; 145 char buf[16]; 146 char* p = buf; 147 148 for (;;) { 149 ch = '0' + (number / divisor); 150 number %= divisor; 151 if (ch != '0') 152 padding = 0; 153 if (!padding || divisor == 1) 154 *(p++) = ch; 155 if (divisor == 1) 156 break; 157 divisor /= 10; 158 } 159 160 size_t numlen = p - buf; 161 if (!fieldWidth || fieldWidth < numlen) 162 fieldWidth = numlen; 163 if (!leftPad) { 164 for (unsigned i = 0; i < fieldWidth - numlen; ++i) { 165 putch(bufptr, zeroPad ? '0' : ' '); 166 } 167 } 168 for (unsigned i = 0; i < numlen; ++i) { 169 putch(bufptr, buf[i]); 170 } 171 if (leftPad) { 172 for (unsigned i = 0; i < fieldWidth - numlen; ++i) { 173 putch(bufptr, ' '); 174 } 175 } 176 177 return fieldWidth; 178} 179 180template<typename PutChFunc> 181[[gnu::always_inline]] inline int print_double(PutChFunc putch, char*& bufptr, double number, bool leftPad, bool zeroPad, u32 fieldWidth, u32 fraction_length = 6) 182{ 183 int length = 0; 184 185 if (number < 0) { 186 putch(bufptr, '-'); 187 length++; 188 number = 0 - number; 189 } 190 191 length = print_u64(putch, bufptr, (i64)number, leftPad, zeroPad, fieldWidth); 192 putch(bufptr, '.'); 193 length++; 194 double fraction = number - (i64)number; 195 196 for (u32 i = 0; i < fraction_length; ++i) 197 fraction = fraction * 10; 198 199 return length + print_u64(putch, bufptr, (i64)fraction, false, true, fraction_length); 200} 201 202template<typename PutChFunc> 203[[gnu::always_inline]] inline int print_i64(PutChFunc putch, char*& bufptr, i64 number, bool leftPad, bool zeroPad, u32 fieldWidth) 204{ 205 if (number < 0) { 206 putch(bufptr, '-'); 207 return print_u64(putch, bufptr, 0 - number, leftPad, zeroPad, fieldWidth) + 1; 208 } 209 return print_u64(putch, bufptr, number, leftPad, zeroPad, fieldWidth); 210} 211 212template<typename PutChFunc> 213[[gnu::always_inline]] inline int print_octal_number(PutChFunc putch, char*& bufptr, u32 number, bool leftPad, bool zeroPad, u32 fieldWidth) 214{ 215 u32 divisor = 134217728; 216 char ch; 217 char padding = 1; 218 char buf[32]; 219 char* p = buf; 220 221 for (;;) { 222 ch = '0' + (number / divisor); 223 number %= divisor; 224 if (ch != '0') 225 padding = 0; 226 if (!padding || divisor == 1) 227 *(p++) = ch; 228 if (divisor == 1) 229 break; 230 divisor /= 8; 231 } 232 233 size_t numlen = p - buf; 234 if (!fieldWidth || fieldWidth < numlen) 235 fieldWidth = numlen; 236 if (!leftPad) { 237 for (unsigned i = 0; i < fieldWidth - numlen; ++i) { 238 putch(bufptr, zeroPad ? '0' : ' '); 239 } 240 } 241 for (unsigned i = 0; i < numlen; ++i) { 242 putch(bufptr, buf[i]); 243 } 244 if (leftPad) { 245 for (unsigned i = 0; i < fieldWidth - numlen; ++i) { 246 putch(bufptr, ' '); 247 } 248 } 249 250 return fieldWidth; 251} 252 253template<typename PutChFunc> 254[[gnu::always_inline]] inline int print_string(PutChFunc putch, char*& bufptr, const char* str, bool left_pad, size_t field_width, bool dot) 255{ 256 size_t len = strlen(str); 257 if (!dot && (!field_width || field_width < len)) 258 field_width = len; 259 size_t pad_amount = field_width > len ? field_width - len : 0; 260 261 if (!left_pad) { 262 for (size_t i = 0; i < pad_amount; ++i) 263 putch(bufptr, ' '); 264 } 265 for (size_t i = 0; i < min(len, field_width); ++i) { 266 putch(bufptr, str[i]); 267 } 268 if (left_pad) { 269 for (size_t i = 0; i < pad_amount; ++i) 270 putch(bufptr, ' '); 271 } 272 return field_width; 273} 274 275template<typename PutChFunc> 276[[gnu::always_inline]] inline int print_signed_number(PutChFunc putch, char*& bufptr, int number, bool leftPad, bool zeroPad, u32 fieldWidth, bool always_sign) 277{ 278 if (number < 0) { 279 putch(bufptr, '-'); 280 return print_number(putch, bufptr, 0 - number, leftPad, zeroPad, fieldWidth) + 1; 281 } 282 if (always_sign) 283 putch(bufptr, '+'); 284 return print_number(putch, bufptr, number, leftPad, zeroPad, fieldWidth); 285} 286 287template<typename PutChFunc> 288[[gnu::always_inline]] inline int printf_internal(PutChFunc putch, char* buffer, const char*& fmt, va_list ap) 289{ 290 const char* p; 291 292 int ret = 0; 293 char* bufptr = buffer; 294 295 for (p = fmt; *p; ++p) { 296 bool left_pad = false; 297 bool zeroPad = false; 298 bool dot = false; 299 unsigned fieldWidth = 0; 300 unsigned fraction_length = 0; 301 unsigned long_qualifiers = 0; 302 bool size_qualifier = false; 303 (void)size_qualifier; 304 bool alternate_form = 0; 305 bool always_sign = false; 306 if (*p == '%' && *(p + 1)) { 307 one_more: 308 ++p; 309 if (*p == '.') { 310 dot = true; 311 if (*(p + 1)) 312 goto one_more; 313 } 314 if (*p == '-') { 315 left_pad = true; 316 if (*(p + 1)) 317 goto one_more; 318 } 319 if (*p == '+') { 320 always_sign = true; 321 if (*(p + 1)) 322 goto one_more; 323 } 324 if (!zeroPad && !fieldWidth && *p == '0') { 325 zeroPad = true; 326 if (*(p + 1)) 327 goto one_more; 328 } 329 if (*p >= '0' && *p <= '9') { 330 if (!dot) { 331 fieldWidth *= 10; 332 fieldWidth += *p - '0'; 333 if (*(p + 1)) 334 goto one_more; 335 } else { 336 fraction_length *= 10; 337 fraction_length += *p - '0'; 338 if (*(p + 1)) 339 goto one_more; 340 } 341 } 342 if (*p == '*') { 343 fieldWidth = va_arg(ap, int); 344 if (*(p + 1)) 345 goto one_more; 346 } 347 if (*p == 'l') { 348 ++long_qualifiers; 349 if (*(p + 1)) 350 goto one_more; 351 } 352 if (*p == 'z') { 353 size_qualifier = true; 354 if (*(p + 1)) 355 goto one_more; 356 } 357 if (*p == '#') { 358 alternate_form = true; 359 if (*(p + 1)) 360 goto one_more; 361 } 362 switch (*p) { 363 case 's': { 364 const char* sp = va_arg(ap, const char*); 365 ret += print_string(putch, bufptr, sp ? sp : "(null)", left_pad, fieldWidth, dot); 366 } break; 367 368 case 'd': 369 case 'i': 370 if (long_qualifiers >= 2) 371 ret += print_i64(putch, bufptr, va_arg(ap, i64), left_pad, zeroPad, fieldWidth); 372 else 373 ret += print_signed_number(putch, bufptr, va_arg(ap, int), left_pad, zeroPad, fieldWidth, always_sign); 374 break; 375 376 case 'u': 377 if (long_qualifiers >= 2) 378 ret += print_u64(putch, bufptr, va_arg(ap, u64), left_pad, zeroPad, fieldWidth); 379 else 380 ret += print_number(putch, bufptr, va_arg(ap, u32), left_pad, zeroPad, fieldWidth); 381 break; 382 383 case 'Q': 384 ret += print_u64(putch, bufptr, va_arg(ap, u64), left_pad, zeroPad, fieldWidth); 385 break; 386 387 case 'q': 388 ret += print_hex(putch, bufptr, va_arg(ap, u64), false, false, left_pad, zeroPad, 16); 389 break; 390 391#if !defined(BOOTSTRAPPER) && !defined(KERNEL) 392 case 'g': 393 case 'f': 394 ret += print_double(putch, bufptr, va_arg(ap, double), left_pad, zeroPad, fieldWidth, fraction_length); 395 break; 396#endif 397 398 case 'o': 399 if (alternate_form) { 400 putch(bufptr, '0'); 401 ++ret; 402 } 403 ret += print_octal_number(putch, bufptr, va_arg(ap, u32), left_pad, zeroPad, fieldWidth); 404 break; 405 406 case 'X': 407 case 'x': 408 ret += print_hex(putch, bufptr, va_arg(ap, u32), *p == 'X', alternate_form, left_pad, zeroPad, fieldWidth); 409 break; 410 411 case 'w': 412 ret += print_hex(putch, bufptr, va_arg(ap, int), false, alternate_form, false, true, 4); 413 break; 414 415 case 'b': 416 ret += print_hex(putch, bufptr, va_arg(ap, int), false, alternate_form, false, true, 2); 417 break; 418 419 case 'c': { 420 char s[2] { (char)va_arg(ap, int), 0 }; 421 ret += print_string(putch, bufptr, s, left_pad, fieldWidth, dot); 422 } break; 423 424 case '%': 425 putch(bufptr, '%'); 426 ++ret; 427 break; 428 429 case 'P': 430 case 'p': 431 ret += print_hex(putch, bufptr, va_arg(ap, u32), *p == 'P', true, false, true, 8); 432 break; 433 434 case 'n': 435 *va_arg(ap, int*) = ret; 436 break; 437 438 default: 439 dbg() << "printf_internal: Unimplemented format specifier " << *p << " (fmt: " << fmt << ")"; 440 } 441 } else { 442 putch(bufptr, *p); 443 ++ret; 444 } 445 } 446 return ret; 447}