aboutsummaryrefslogtreecommitdiff
path: root/include/fmt.h
blob: f4be0b130e45534a206a535556d0970928baff09 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// Copyright (c) 2020 Johannes Stoelp

#pragma once

#include <stdarg.h>

#define ALLOW_UNUSED __attribute__((unused))

static const char* num2dec(char* buf, unsigned long len, unsigned long long num);
static const char* num2hex(char* buf, unsigned long len, unsigned long long num);

ALLOW_UNUSED
static int vfmt(char* buf, unsigned long len, const char* fmt, va_list ap) {
    unsigned i = 0;

#define put(c)           \
    {                    \
        char _c = (c);   \
        if (i < len) {   \
            buf[i] = _c; \
        }                \
        ++i;             \
    }

#define puts(s)    \
    while (*s) {   \
        put(*s++); \
    }

    char scratch[16];
    int l_cnt = 0;

    while (*fmt) {
        if (*fmt != '%') {
            put(*fmt++);
            continue;
        }

        l_cnt = 0;

    continue_fmt:
        switch (*(++fmt /* constume '%' */)) {
            case 'l':
                ++l_cnt;
                goto continue_fmt;
            case 'd': {
                long val = l_cnt > 0 ? va_arg(ap, long) : va_arg(ap, int);
                if (val < 0) {
                    val *= -1;
                    put('-');
                }
                const char* ptr = num2dec(scratch, sizeof(scratch), val);
                puts(ptr);
            } break;
            case 'x': {
                unsigned long val = l_cnt > 0 ? va_arg(ap, unsigned long) : va_arg(ap, unsigned);
                const char* ptr = num2hex(scratch, sizeof(scratch), val);
                puts(ptr);
            } break;
            case 's': {
                const char* ptr = va_arg(ap, const char*);
                puts(ptr);
            } break;
            case 'p': {
                const void* val = va_arg(ap, const void*);
                const char* ptr = num2hex(scratch, sizeof(scratch), (unsigned long long)val);
                put('0');
                put('x');
                puts(ptr);
            } break;
            default:
                put(*fmt);
                break;
        }
        ++fmt;
    }

#undef puts
#undef put

    if (buf) {
        i < len ? (buf[i] = '\0') : (buf[len - 1] = '\0');
    }
    return i;
}

ALLOW_UNUSED
static int fmt(char* buf, unsigned long len, const char* fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    int ret = vfmt(buf, len, fmt, ap);
    va_end(ap);
    return ret;
}

/// Internal Helper

ALLOW_UNUSED
static const char* num2dec(char* buf, unsigned long len, unsigned long long num) {
    char* pbuf = buf + len - 1;
    *pbuf = '\0';

    if (num == 0) {
        *(--pbuf) = '0';
    }

    while (num > 0 && pbuf != buf) {
        char d = (num % 10) + '0';
        *(--pbuf) = d;
        num /= 10;
    }
    return pbuf;
}

ALLOW_UNUSED
static const char* num2hex(char* buf, unsigned long len, unsigned long long num) {
    char* pbuf = buf + len - 1;
    *pbuf = '\0';

    if (num == 0) {
        *(--pbuf) = '0';
    }

    while (num > 0 && pbuf != buf) {
        char d = (num & 0xf);
        *(--pbuf) = d + (d > 9 ? 'a' - 10 : '0');
        num >>= 4;
    }
    return pbuf;
}