#include #include #include #include #include #include #include #include #include typedef long int ssize_t; static void __strrev(char* arr, int size) { int left = 0; int right = size - 1; for (int i = left; i < right; i++) { char temp = arr[i]; arr[i] = arr[right]; arr[right] = temp; right--; } } template static char* __unsignedtoa(IntegerType number, char* arr, int base) { int i = 0; if (number == 0) { arr[i] = '0'; arr[i + 1] = '\0'; return arr; } while (number != 0) { IntegerType r = number % (IntegerType)base; arr[i] = (char)((r > 9) ? (r - 10) + 'a' : r + '0'); i++; number /= base; } __strrev(arr, i); arr[i] = '\0'; return arr; } template static char* __signedtoa(IntegerType number, char* arr, int base) { int i = 0, negative = 0; if (number == 0) { arr[i] = '0'; arr[i + 1] = '\0'; return arr; } if (number < 0 && base == 10) { number *= -1; negative = 1; } while (number != 0) { IntegerType r = number % base; arr[i] = (char)((r > 9) ? (r - 10) + 'a' : r + '0'); i++; number /= base; } if (negative) { arr[i] = '-'; i++; } __strrev(arr, i); arr[i] = '\0'; return arr; } #pragma GCC push_options #pragma GCC optimize("O0") template static int internal_printf(const char* format, PutString put_string_callback, ssize_t max, va_list ap) { char buffer[1025]; // 1024 with null terminator size_t format_size = strlen(format); size_t format_index = 0; size_t buffer_insert_index = 0; ssize_t max_remaining = max; size_t written = 0; auto flush_buffer = [&]() { size_t buffer_length = buffer_insert_index; written += buffer_length; buffer_insert_index = 0; if (max_remaining < 0) { buffer[buffer_length] = 0; put_string_callback(buffer); return; } if (max_remaining == 0) { return; } if (buffer_length <= (size_t)max_remaining) { max_remaining -= buffer_length; buffer[buffer_length] = 0; put_string_callback(buffer); } else { buffer[max_remaining] = 0; max_remaining = 0; put_string_callback(buffer); } }; bool is_long = false; bool is_unsigned_long = false; bool preserve_format = false; while (format_index < format_size) { char current_char = format[format_index]; if (current_char == '%' || preserve_format) { if (!preserve_format && format_index + 1 == format_size) // end of format string { format_index++; continue; } else { if (!preserve_format) format_index++; preserve_format = false; current_char = format[format_index]; switch (current_char) { case 'c': { buffer[buffer_insert_index++] = (char)va_arg(ap, int); if (buffer_insert_index == 1024) flush_buffer(); break; } case '%': { buffer[buffer_insert_index++] = '%'; if (buffer_insert_index == 1024) flush_buffer(); break; } case 'z': { is_unsigned_long = true; preserve_format = true; break; } case 'l': { is_long = true; preserve_format = true; break; } case 'd': { if (is_unsigned_long) { char result[25]; __unsignedtoa(va_arg(ap, uint64_t), result, 10); if (buffer_insert_index + strlen(result) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, result, strlen(result)); buffer_insert_index += strlen(result); if (buffer_insert_index == 1024) flush_buffer(); is_unsigned_long = is_long = false; } else if (is_long) { char result[25]; __signedtoa(va_arg(ap, int64_t), result, 10); if (buffer_insert_index + strlen(result) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, result, strlen(result)); buffer_insert_index += strlen(result); if (buffer_insert_index == 1024) flush_buffer(); is_unsigned_long = is_long = false; } else { char result[25]; __signedtoa(va_arg(ap, int32_t), result, 10); if (buffer_insert_index + strlen(result) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, result, strlen(result)); buffer_insert_index += strlen(result); if (buffer_insert_index == 1024) flush_buffer(); } break; } case 'u': { if (is_unsigned_long || is_long) { char result[25]; __unsignedtoa(va_arg(ap, uint64_t), result, 10); if (buffer_insert_index + strlen(result) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, result, strlen(result)); buffer_insert_index += strlen(result); if (buffer_insert_index == 1024) flush_buffer(); is_unsigned_long = is_long = false; } else { char result[25]; __unsignedtoa(va_arg(ap, uint32_t), result, 10); if (buffer_insert_index + strlen(result) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, result, strlen(result)); buffer_insert_index += strlen(result); if (buffer_insert_index == 1024) flush_buffer(); } break; } case 'x': { if (is_unsigned_long || is_long) { char result[25]; __unsignedtoa(va_arg(ap, uint64_t), result, 16); if (buffer_insert_index + strlen(result) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, result, strlen(result)); buffer_insert_index += strlen(result); if (buffer_insert_index == 1024) flush_buffer(); is_unsigned_long = is_long = false; } else { char result[25]; __unsignedtoa(va_arg(ap, uint32_t), result, 16); if (buffer_insert_index + strlen(result) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, result, strlen(result)); buffer_insert_index += strlen(result); if (buffer_insert_index == 1024) flush_buffer(); } break; } case 'p': { char result[25]; __unsignedtoa(va_arg(ap, uint64_t), result, 16); if (buffer_insert_index + strlen(result) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, result, strlen(result)); buffer_insert_index += strlen(result); if (buffer_insert_index == 1024) flush_buffer(); break; } case 'm': { const char* str = strerror(errno); while (strlen(str) > 1024) { flush_buffer(); memcpy(buffer, str, 1024); str += 1024; buffer_insert_index = 1024; } if (buffer_insert_index + strlen(str) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, str, strlen(str)); buffer_insert_index += strlen(str); if (buffer_insert_index == 1024) flush_buffer(); break; } case 's': { const char* str = va_arg(ap, const char*); while (strlen(str) > 1024) { flush_buffer(); memcpy(buffer, str, 1024); str += 1024; buffer_insert_index = 1024; } if (buffer_insert_index + strlen(str) > 1024) flush_buffer(); memcpy(buffer + buffer_insert_index, str, strlen(str)); buffer_insert_index += strlen(str); if (buffer_insert_index == 1024) flush_buffer(); break; } default: { NOT_IMPLEMENTED("internal_printf: unknown format specifier"); } } } } else { buffer[buffer_insert_index++] = current_char; if (buffer_insert_index == 1024) flush_buffer(); } format_index++; } if (buffer_insert_index > 0) flush_buffer(); return (int)written; } #pragma GCC pop_options extern "C" { int vprintf(const char* format, va_list ap) { return vfprintf(stdout, format, ap); } int vsprintf(char* str, const char* format, va_list ap) { return internal_printf( format, [&](const char* s) { if (str) strncat(str, s, 1025); }, -1, ap); } int vsnprintf(char* str, size_t max, const char* format, va_list ap) { return internal_printf( format, [&](const char* s) { if (str) strncat(str, s, 1025); }, max == 0 ? 0 : max - 1, ap); } int snprintf(char* str, size_t max, const char* format, ...) { va_list ap; va_start(ap, format); int written = vsnprintf(str, max, format, ap); va_end(ap); return written; } int sprintf(char* str, const char* format, ...) { va_list ap; va_start(ap, format); int written = vsprintf(str, format, ap); va_end(ap); return written; } int printf(const char* format, ...) { va_list ap; va_start(ap, format); int written = vfprintf(stdout, format, ap); va_end(ap); return written; } int fprintf(FILE* stream, const char* format, ...) { va_list ap; va_start(ap, format); int written = vfprintf(stream, format, ap); va_end(ap); return written; } int vfprintf(FILE* stream, const char* format, va_list ap) { return internal_printf( format, [&](const char* s) { fwrite(s, strlen(s), 1, stream); }, -1, ap); } }