/** @file format.c * Module handling various formating functions * * The module provides a lightweight printf function and the attached tools */ //--includes-------------------------------------------------------------------- #include "format.h" //--local definitions----------------------------------------------------------- struct BufferArg { char* restrict buffer; uint32_t buffer_size; uint32_t byte_index; }; static uint32_t buffer_write(uint8_t byte, void* arg); static uint32_t render_format(FormatCallback callback, void* arg, const char* restrict format, va_list va); static uint32_t render_signed(FormatCallback callback, void* arg, int32_t number, uint8_t base); static uint32_t render_unsigned(FormatCallback callback, void* arg, uint32_t number, uint8_t base); //--local variables------------------------------------------------------------- //--public functions------------------------------------------------------------ uint32_t format_snprintf(char* restrict buffer, uint32_t buffer_size, const char* restrict format, ...) { va_list va; va_start(va, format); struct BufferArg arg = { buffer, buffer_size, 0 }; uint32_t ret = render_format(buffer_write, &arg, format, va); va_end(va); return ret; } uint32_t format_fctprintf(FormatCallback callback, void* arg, const char* restrict format, ...) { va_list va; va_start(va, format); uint32_t ret = render_format(callback, arg, format, va); va_end(va); return ret; } uint32_t format_vfctprintf(FormatCallback callback, void* arg, const char* restrict format, va_list va) { return render_format(callback, arg, format, va); } //--local functions------------------------------------------------------------- static uint32_t buffer_write(uint8_t byte, void* arg) { struct BufferArg* buffer_arg = (struct BufferArg*)arg; if (buffer_arg->byte_index >= buffer_arg->buffer_size) { return 1; } buffer_arg->buffer[buffer_arg->byte_index] = byte; ++buffer_arg->byte_index; return 0; } static uint32_t render_format(FormatCallback callback, void* arg, const char* restrict format, va_list va) { bool in_format = false; for (const char* c = format; *c != '\0'; ++c) { if (in_format) { switch (*c) { case '%': callback('%', arg); break; case 'c': callback((uint8_t)va_arg(va, uint32_t), arg); break; case 's': { const char* str = va_arg(va, const char*); for (const char* c2 = str; *c2 != '\0'; ++c2) { callback(*c2, arg); } } break; case 'i': render_signed(callback, arg, va_arg(va, int32_t), 10); break; case 'u': render_unsigned(callback, arg, va_arg(va, uint32_t), 10); break; case 'x': render_unsigned(callback, arg, va_arg(va, uint32_t), 16); break; default: //TODO manage error break; } } else { switch (*c) { case '%': in_format = true; continue; default: callback(*c, arg); break; } } in_format = false; } return 0; } static uint32_t render_signed(FormatCallback callback, void* arg, int32_t number, uint8_t base) { if (number < 0) { callback('-', arg); number = -number; } return render_unsigned(callback, arg, (uint32_t)number, base); } static uint32_t render_unsigned(FormatCallback callback, void* arg, uint32_t number, uint8_t base) { uint32_t base_power = base; while (base_power <= number) { base_power *= base; } while (base_power > 1) { base_power /= base; uint8_t digit = 0; while (number >= base_power) { number -= base_power; ++digit; } if (digit < 10) { callback('0' + digit, arg); } else { callback('A' + digit - 10, arg); } } return 0; }