about summary refs log tree commit diff
path: root/src/pOS/include/libc/printf_internal.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/pOS/include/libc/printf_internal.h')
-rw-r--r--src/pOS/include/libc/printf_internal.h409
1 files changed, 409 insertions, 0 deletions
diff --git a/src/pOS/include/libc/printf_internal.h b/src/pOS/include/libc/printf_internal.h
new file mode 100644
index 0000000..c2ae2fd
--- /dev/null
+++ b/src/pOS/include/libc/printf_internal.h
@@ -0,0 +1,409 @@
+#ifndef _PRINTF_INTERNAL_H_
+#define _PRINTF_INTERNAL_H_
+
+#include <stdarg.h>
+#include <string.h>
+#include <math.h>
+
+/* FROM SERENITYOS */
+
+static constexpr const char* printf_hex_digits_lower = "0123456789abcdef";
+static constexpr const char* printf_hex_digits_upper = "0123456789ABCDEF";
+
+template<typename PutChFunc, typename T>
+inline int print_hex(PutChFunc putch, char*& bufptr, T number, bool upper_case, bool alternate_form, bool left_pad, bool zero_pad, uint8_t field_width)
+{
+    int ret = 0;
+
+    int digits = 0;
+    for (T n = number; n > 0; n >>= 4)
+        ++digits;
+    if (digits == 0)
+        digits = 1;
+
+    if (left_pad) {
+        int stop_at = field_width - digits;
+        if (alternate_form)
+            stop_at -= 2;
+
+        while (ret < stop_at) {
+            putch(bufptr, ' ');
+            ++ret;
+        }
+    }
+
+    if (alternate_form) {
+        putch(bufptr, '0');
+        putch(bufptr, 'x');
+        ret += 2;
+        field_width += 2;
+    }
+
+    if (zero_pad) {
+        while (ret < field_width - digits) {
+            putch(bufptr, '0');
+            ++ret;
+        }
+    }
+
+    if (number == 0) {
+        putch(bufptr, '0');
+        ++ret;
+    } else {
+        uint8_t shift_count = digits * 4;
+        while (shift_count) {
+            shift_count -= 4;
+            putch(bufptr,
+                upper_case
+                    ? printf_hex_digits_upper[(number >> shift_count) & 0x0f]
+                    : printf_hex_digits_lower[(number >> shift_count) & 0x0f]);
+            ++ret;
+        }
+    }
+
+    return ret;
+}
+
+template<typename PutChFunc>
+inline int print_number(PutChFunc putch, char*& bufptr, uint32_t number, bool left_pad, bool zero_pad, uint32_t field_width)
+{
+    uint32_t divisor = 1000000000;
+    char ch;
+    char padding = 1;
+    char buf[16];
+    char* p = buf;
+
+    for (;;) {
+        ch = '0' + (number / divisor);
+        number %= divisor;
+        if (ch != '0')
+            padding = 0;
+        if (!padding || divisor == 1)
+            *(p++) = ch;
+        if (divisor == 1)
+            break;
+        divisor /= 10;
+    }
+
+    size_t numlen = p - buf;
+    if (!field_width || field_width < numlen)
+        field_width = numlen;
+    if (!left_pad) {
+        for (unsigned i = 0; i < field_width - numlen; ++i) {
+            putch(bufptr, zero_pad ? '0' : ' ');
+        }
+    }
+    for (unsigned i = 0; i < numlen; ++i) {
+        putch(bufptr, buf[i]);
+    }
+    if (left_pad) {
+        for (unsigned i = 0; i < field_width - numlen; ++i) {
+            putch(bufptr, ' ');
+        }
+    }
+
+    return field_width;
+}
+
+template<typename PutChFunc>
+inline int print_uint64_t(PutChFunc putch, char*& bufptr, uint64_t number, bool left_pad, bool zero_pad, uint32_t field_width)
+{
+    uint64_t divisor = 10000000000000000000LLU;
+    char ch;
+    char padding = 1;
+    char buf[16];
+    char* p = buf;
+
+    for (;;) {
+        ch = '0' + (number / divisor);
+        number %= divisor;
+        if (ch != '0')
+            padding = 0;
+        if (!padding || divisor == 1)
+            *(p++) = ch;
+        if (divisor == 1)
+            break;
+        divisor /= 10;
+    }
+
+    size_t numlen = p - buf;
+    if (!field_width || field_width < numlen)
+        field_width = numlen;
+    if (!left_pad) {
+        for (unsigned i = 0; i < field_width - numlen; ++i) {
+            putch(bufptr, zero_pad ? '0' : ' ');
+        }
+    }
+    for (unsigned i = 0; i < numlen; ++i) {
+        putch(bufptr, buf[i]);
+    }
+    if (left_pad) {
+        for (unsigned i = 0; i < field_width - numlen; ++i) {
+            putch(bufptr, ' ');
+        }
+    }
+
+    return field_width;
+}
+
+template<typename PutChFunc>
+inline int print_double(PutChFunc putch, char*& bufptr, double number, bool left_pad, bool zero_pad, uint32_t field_width, uint32_t fraction_length = 6)
+{
+    int length = 0;
+
+    if (number < 0) {
+        putch(bufptr, '-');
+        length++;
+        number = 0 - number;
+    }
+
+    length = print_uint64_t(putch, bufptr, (int64_t)number, left_pad, zero_pad, field_width);
+    putch(bufptr, '.');
+    length++;
+    double fraction = number - (int64_t)number;
+
+    for (uint32_t i = 0; i < fraction_length; ++i)
+        fraction = fraction * 10;
+
+    return length + print_uint64_t(putch, bufptr, (int64_t)fraction, false, true, fraction_length);
+}
+
+template<typename PutChFunc>
+inline int print_int64_t(PutChFunc putch, char*& bufptr, int64_t number, bool left_pad, bool zero_pad, uint32_t field_width)
+{
+    if (number < 0) {
+        putch(bufptr, '-');
+        return print_uint64_t(putch, bufptr, 0 - number, left_pad, zero_pad, field_width) + 1;
+    }
+    return print_uint64_t(putch, bufptr, number, left_pad, zero_pad, field_width);
+}
+
+template<typename PutChFunc>
+inline int print_octal_number(PutChFunc putch, char*& bufptr, uint32_t number, bool left_pad, bool zero_pad, uint32_t field_width)
+{
+    uint32_t divisor = 134217728;
+    char ch;
+    char padding = 1;
+    char buf[32];
+    char* p = buf;
+
+    for (;;) {
+        ch = '0' + (number / divisor);
+        number %= divisor;
+        if (ch != '0')
+            padding = 0;
+        if (!padding || divisor == 1)
+            *(p++) = ch;
+        if (divisor == 1)
+            break;
+        divisor /= 8;
+    }
+
+    size_t numlen = p - buf;
+    if (!field_width || field_width < numlen)
+        field_width = numlen;
+    if (!left_pad) {
+        for (unsigned i = 0; i < field_width - numlen; ++i) {
+            putch(bufptr, zero_pad ? '0' : ' ');
+        }
+    }
+    for (unsigned i = 0; i < numlen; ++i) {
+        putch(bufptr, buf[i]);
+    }
+    if (left_pad) {
+        for (unsigned i = 0; i < field_width - numlen; ++i) {
+            putch(bufptr, ' ');
+        }
+    }
+
+    return field_width;
+}
+
+template<typename PutChFunc>
+inline int print_string(PutChFunc putch, char*& bufptr, const char* str, size_t len, bool left_pad, size_t field_width, bool dot)
+{
+    if (!dot && (!field_width || field_width < len))
+        field_width = len;
+    size_t pad_amount = field_width > len ? field_width - len : 0;
+
+    if (!left_pad) {
+        for (size_t i = 0; i < pad_amount; ++i)
+            putch(bufptr, ' ');
+    }
+    for (int i = 0; i < min(len, field_width); ++i) {
+        putch(bufptr, str[i]);
+    }
+    if (left_pad) {
+        for (size_t i = 0; i < pad_amount; ++i)
+            putch(bufptr, ' ');
+    }
+    return field_width;
+}
+
+template<typename PutChFunc>
+inline int print_signed_number(PutChFunc putch, char*& bufptr, int number, bool left_pad, bool zero_pad, uint32_t field_width, bool always_sign)
+{
+    if (number < 0) {
+        putch(bufptr, '-');
+        return print_number(putch, bufptr, 0 - number, left_pad, zero_pad, field_width) + 1;
+    }
+    if (always_sign)
+        putch(bufptr, '+');
+    return print_number(putch, bufptr, number, left_pad, zero_pad, field_width);
+}
+
+template<typename PutChFunc>
+inline int printf_internal(PutChFunc putch, char* buffer, const char*& fmt, va_list ap)
+{
+    const char* p;
+
+    int ret = 0;
+    char* bufptr = buffer;
+
+    for (p = fmt; *p; ++p) {
+        bool left_pad = false;
+        bool zero_pad = false;
+        bool dot = false;
+        unsigned field_width = 0;
+        unsigned fraction_length = 0;
+        unsigned long_qualifiers = 0;
+        bool size_qualifier = false;
+        (void)size_qualifier;
+        bool alternate_form = 0;
+        bool always_sign = false;
+        if (*p == '%' && *(p + 1)) {
+        one_more:
+            ++p;
+            if (*p == '.') {
+                dot = true;
+                if (*(p + 1))
+                    goto one_more;
+            }
+            if (*p == '-') {
+                left_pad = true;
+                if (*(p + 1))
+                    goto one_more;
+            }
+            if (*p == '+') {
+                always_sign = true;
+                if (*(p + 1))
+                    goto one_more;
+            }
+            if (!zero_pad && !field_width && *p == '0') {
+                zero_pad = true;
+                if (*(p + 1))
+                    goto one_more;
+            }
+            if (*p >= '0' && *p <= '9') {
+                if (!dot) {
+                    field_width *= 10;
+                    field_width += *p - '0';
+                    if (*(p + 1))
+                        goto one_more;
+                } else {
+                    fraction_length *= 10;
+                    fraction_length += *p - '0';
+                    if (*(p + 1))
+                        goto one_more;
+                }
+            }
+            if (*p == '*') {
+                field_width = va_arg(ap, int);
+                if (*(p + 1))
+                    goto one_more;
+            }
+            if (*p == 'l') {
+                ++long_qualifiers;
+                if (*(p + 1))
+                    goto one_more;
+            }
+            if (*p == 'z') {
+                size_qualifier = true;
+                if (*(p + 1))
+                    goto one_more;
+            }
+            if (*p == '#') {
+                alternate_form = true;
+                if (*(p + 1))
+                    goto one_more;
+            }
+            switch (*p) {
+            case 's': {
+                const char* sp = va_arg(ap, const char*);
+                if (!sp)
+                    sp = "(null)";
+                ret += print_string(putch, bufptr, sp, strlen(sp), left_pad, field_width, dot);
+            } break;
+
+            case 'd':
+            case 'i':
+                if (long_qualifiers >= 2)
+                    ret += print_int64_t(putch, bufptr, va_arg(ap, int64_t), left_pad, zero_pad, field_width);
+                else
+                    ret += print_signed_number(putch, bufptr, va_arg(ap, int), left_pad, zero_pad, field_width, always_sign);
+                break;
+
+            case 'u':
+                if (long_qualifiers >= 2)
+                    ret += print_uint64_t(putch, bufptr, va_arg(ap, uint64_t), left_pad, zero_pad, field_width);
+                else
+                    ret += print_number(putch, bufptr, va_arg(ap, uint32_t), left_pad, zero_pad, field_width);
+                break;
+
+            case 'Q':
+                ret += print_uint64_t(putch, bufptr, va_arg(ap, uint64_t), left_pad, zero_pad, field_width);
+                break;
+
+            case 'q':
+                ret += print_hex(putch, bufptr, va_arg(ap, uint64_t), false, false, left_pad, zero_pad, 16);
+                break;
+
+            case 'o':
+                if (alternate_form) {
+                    putch(bufptr, '0');
+                    ++ret;
+                }
+                ret += print_octal_number(putch, bufptr, va_arg(ap, uint32_t), left_pad, zero_pad, field_width);
+                break;
+
+            case 'X':
+            case 'x':
+                ret += print_hex(putch, bufptr, va_arg(ap, uint32_t), *p == 'X', alternate_form, left_pad, zero_pad, field_width);
+                break;
+
+            case 'w':
+                ret += print_hex(putch, bufptr, va_arg(ap, int), false, alternate_form, false, true, 4);
+                break;
+
+            case 'b':
+                ret += print_hex(putch, bufptr, va_arg(ap, int), false, alternate_form, false, true, 2);
+                break;
+
+            case 'c': {
+                char c = va_arg(ap, int);
+                ret += print_string(putch, bufptr, &c, 1, left_pad, field_width, dot);
+            } break;
+
+            case '%':
+                putch(bufptr, '%');
+                ++ret;
+                break;
+
+            case 'P':
+            case 'p':
+                ret += print_hex(putch, bufptr, va_arg(ap, uint32_t), *p == 'P', true, false, true, 8);
+                break;
+
+            case 'n':
+                *va_arg(ap, int*) = ret;
+                break;
+            }
+        } else {
+            putch(bufptr, *p);
+            ++ret;
+        }
+    }
+    return ret;
+}
+
+#endif