diff options
Diffstat (limited to 'src/user/knob/format.c')
-rw-r--r-- | src/user/knob/format.c | 257 |
1 files changed, 220 insertions, 37 deletions
diff --git a/src/user/knob/format.c b/src/user/knob/format.c index 593b20c..54d50ef 100644 --- a/src/user/knob/format.c +++ b/src/user/knob/format.c @@ -1,48 +1,231 @@ -#include <stdbool.h> -#include <stdint.h> - -bool try_sntoi(const char *s, uint32_t n, uint32_t *out) { - uint32_t calc = 0; - for (uint32_t i = 0; i < n; ++i) { - if ((s[i] < '0') || (s[i] > '9')) - return false; - calc = calc * 10 + s[i] - '0'; - } - *out = calc; - return true; -} +#include <knob/block.h> +#include <knob/panic.h> +#include <knob/heap.h> + +#include <pland/syscall.h> +#include <pland/pcrt.h> + +#include <stdarg.h> + +#define FORMAT_BUF_INIT_SIZE 200 +#define FORMAT_BUF_CHUNK_SIZE 50 -__attribute__ ((access (write_only, 2))) -void itosz(uint32_t i, char *out) { - if (!i) { - *(uint16_t *)out = (uint16_t)'0'; +#define BAD_SPEC "%%UNKNOWN FORMAT SPEC%%" +#define BAD_SPEC_LEN 23 + +static char *buf; +static uint32_t buf_s; +static char *buf_i; + +static const char *const hextab = "0123456789abcdef"; + +static void ensure(uint32_t extra) { + const uint32_t total_len = buf_i - buf + extra; + if (total_len < buf_s) return; + + buf_s = (total_len / FORMAT_BUF_CHUNK_SIZE + 1) * FORMAT_BUF_CHUNK_SIZE; + + char *const new_buf = get_block(buf_s); + if (!new_buf) + PANIC("out of memory in knob format"); + blockcpy(new_buf, buf, buf_i - buf); + free_block(buf); + buf_i += new_buf - buf; + buf = new_buf; +} + +struct format_spec { + uint32_t len; + enum { + UNKNOWN, + CHAR, + STRING, + UNSIGNED_DECIMAL, + HEXADECIMAL + //TODO: signed decimal + } kind; +}; + +static const char *get_format(const char *from, struct format_spec *format_out) { + if (*from == 'n') { + ++from; + format_out->len = -1; } - bool zero = false; - for (uint32_t m = 1000000000; m; m /= 10) { - uint8_t d = (i / m) % 10; - if (zero) - *(out++) = d + '0'; - else if (d) { - zero = true; - *(out++) = d + '0'; + else { + uint32_t len = 0; + while ((*from >= '0') && (*from <= '9')) + len = len * 10 + *(from++) - '0'; + format_out->len = len; + } + + switch (*from) { + case 'c': + format_out->kind = CHAR; + break; + case 's': + format_out->kind = STRING; + break; + case 'u': + format_out->kind = UNSIGNED_DECIMAL; + break; + case 'h': + case 'x': + format_out->kind = HEXADECIMAL; + break; + default: + format_out->kind = UNKNOWN; + break; + } + + return from + 1; +} + +//allocates new memory +char *format_v(const char *fmt, va_list args) { + buf = get_block(FORMAT_BUF_INIT_SIZE); + if (!buf) + PANIC("out of memory in knob format"); + buf_s = FORMAT_BUF_INIT_SIZE; + buf_i = buf; + + while (*fmt) { + if (*fmt != '%') { + ensure(1); + *(buf_i++) = *(fmt++); + } + else if (fmt[1] == '%') { + ensure(1); + *(buf_i++) = '%'; + fmt += 2; + } + else { + struct format_spec form; + fmt = get_format(fmt + 1, &form); + if (form.len == -1) + //should passing zero still have the special meaning? + form.len = va_arg(args, uint32_t); + switch (form.kind) { + case UNKNOWN: + ensure(BAD_SPEC_LEN); + blockcpy(buf_i, BAD_SPEC, BAD_SPEC_LEN); + buf_i += BAD_SPEC_LEN; + continue; + + uint32_t ch; + case CHAR: + ch = va_arg(args, uint32_t); + ensure(1); + *(buf_i++) = (char)ch; + continue; + + const char *str; + case STRING: + str = va_arg(args, const char *); + if (!form.len) + form.len = strlen(str); + ensure(form.len); + blockcpy(buf_i, str, form.len); + buf_i += form.len; + continue; + + uint32_t k; + case UNSIGNED_DECIMAL: + k = va_arg(args, uint32_t); + if (!form.len) { + uint32_t n = 10; + ++form.len; + while (k >= n) { + ++form.len; + n *= 10; + } + } + ensure(form.len); + const uint32_t len_backup = form.len; + while (form.len--) { + buf_i[form.len] = (k % 10) + '0'; + k /= 10; + } + buf_i += len_backup; + continue; + + case HEXADECIMAL: + k = va_arg(args, uint32_t); + if (!form.len) + form.len = 8; + ensure(form.len); + const uint32_t hlen_backup = form.len; + while (form.len--) { + buf_i[form.len] = hextab[k % 16]; + k >>= 4; + } + buf_i += hlen_backup; + continue; + } } } - *out = '\0'; + + *buf_i = '\0'; + return buf; +} + +//allocates new memory +char *format(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + char *const res = format_v(fmt, args); + va_end(args); + return res; +} + +void syslogf_v(const char *fmt, va_list args) { + char *const msg = format_v(fmt, args); + _system_log(msg); + free_block(msg); } -const char *const hex_digits = "0123456789abcdef"; +void syslogf(const char *fmt, ...) { + va_list args; + va_start(args, fmt); + syslogf_v(fmt, args); + va_end(args); +} -__attribute__ ((access (write_only, 2))) -void itosz_h8(uint8_t i, char *out) { - out[0] = hex_digits[i >> 4]; - out[1] = hex_digits[i & 0xf]; - out[2] = '\0'; +//reads a unsigned decimal terminated by either null or whitespace +//returns length of string plus length of whitespace +//returns 0 on failure +uint32_t try_swtou(const char *from, uint32_t *i_out) { + const char *const old_from = from; + uint32_t v = 0; + while (*from && (*from != '\n') && (*from != ' ')) { + if ((*from < '0') || (*from > '9')) + return 0; + v = v * 10 + *(from++) - '0'; + } + *i_out = v; + while ((*from == '\n') || (*from == ' ')) + ++from; + return from - old_from; } -__attribute__ ((access (write_only, 2))) -void itosz_h32(uint32_t i, char *out) { - for (uint8_t digit = 0; digit < 8; ++digit) - out[digit] = hex_digits[(i >> (28 - digit * 4)) & 0xf]; - out[8] = '\0'; +//reads a hexadecimal terminated by either null or whitespace +//returns length of string plus length of whitespace +//returns 0 on failure +uint32_t try_swtoh(const char *from, uint32_t *i_out) { + const char *const old_from = from; + uint32_t v = 0; + while (*from && (*from != '\n') && (*from != ' ')) { + if ((*from >= '0') && (*from <= '9')) + v = v * 16 + *(from++) - '0'; + else if ((*from >= 'a') && (*from <= 'f')) + v = v * 16 + *(from++) - 'a' + 10; + else if ((*from >= 'A') && (*from <= 'F')) + v = v * 16 + *(from++) - 'A' + 10; + else + return 0; + } + *i_out = v; + while ((*from == '\n') || (*from == ' ')) + ++from; + return from - old_from; }
\ No newline at end of file |