IndustrialDeviceController/Software/MT3620_IDC_RTApp/lib/Print.c (308 lines of code) (raw):
/* Copyright (c) Codethink Ltd. All rights reserved.
Licensed under the MIT License. */
#include "Print.h"
#include <stddef.h>
#define PRINT_MAX_WIDTH 10
#define PRINT_TEMP_PRINTF_BUFFER 256
#define PRINT_FLOAT_BASE 10
#define PRINT_FLOAT_SIGDIG_DEFAULT 6
int32_t UART_Print(UART *handle, const char *msg)
{
if (!msg) {
return ERROR_PARAMETER;
}
return UART_Write(handle, (const uint8_t *)msg, __builtin_strlen(msg));
}
int32_t UART__PrintUIntBaseFiller(
UART *handle,
int32_t value,
unsigned base,
unsigned width,
bool upper,
char filler)
{
if ((base == 0) || (base > 36)) {
return ERROR_UNSUPPORTED;
}
if (width > PRINT_MAX_WIDTH) {
return ERROR_UNSUPPORTED;
}
// Maximum decimal length is ten digits.
char buff[PRINT_MAX_WIDTH];
uint32_t p, w, v;
for (p = PRINT_MAX_WIDTH, w = 0, v = value;
(width == 0 ? ((v != 0) || (w == 0)): (w < width)); w++, v /= base) {
unsigned digit;
if ((value != 0) && (v == 0)) {
digit = filler;
}
else {
digit = (v % base);
if (digit < 10) {
digit += '0';
} else {
digit = (digit - 10) + (upper ? 'A' : 'a');
}
}
buff[--p] = digit;
}
return UART_Write(handle, &buff[p], w);
}
int32_t UART_PrintUIntBase(
UART *handle,
int32_t value,
unsigned base,
unsigned width,
bool upper)
{
return UART__PrintUIntBaseFiller(handle, value, base, width, upper, '0');
}
static inline int32_t UART__PrintIntBaseFiller(
UART *handle,
int32_t value,
unsigned base,
unsigned width,
bool upper,
char filler)
{
if (value < 0) {
int32_t error = UART_Print(handle, "-");
if (error != ERROR_NONE) {
return error;
}
value = -value;
}
return UART__PrintUIntBaseFiller(handle, value, base,
width, upper, filler);
}
int32_t UART_PrintIntBase(
UART *handle,
int32_t value,
unsigned base,
unsigned width,
bool upper)
{
return UART__PrintIntBaseFiller(handle, value, base, width, upper, '0');
}
static inline int32_t Int32_Power(int32_t base, int32_t exp)
{
int32_t result = 1;
for (;;)
{
if (exp & 1)
result *= base;
exp >>= 1;
if (!exp)
break;
base *= base;
}
return result;
}
static inline int32_t UART__PrintFloatFiller(
UART *handle,
float value,
unsigned sigDigits,
unsigned width,
char filler)
{
if (width > PRINT_MAX_WIDTH) {
return ERROR_UNSUPPORTED;
}
int32_t error;
if (value < 0) {
error = UART_Print(handle, "-");
if (error != ERROR_NONE) {
return error;
}
value = -value;
}
if (sigDigits == 0) {
sigDigits = PRINT_FLOAT_SIGDIG_DEFAULT;
}
/* Get LH and RH side of float */
int32_t leftHand, rightHand;
leftHand = (int32_t)value;
rightHand = ((value - leftHand) * Int32_Power(
PRINT_FLOAT_BASE, sigDigits));
int32_t lhWidth;
if ((width == 0) || ((lhWidth = width - 1 - sigDigits) > 0)) {
lhWidth = 0;
}
/* Print LH.RH */
if ((error = UART__PrintUIntBaseFiller(handle, leftHand,
PRINT_FLOAT_BASE, (unsigned)lhWidth,
false, filler)) != ERROR_NONE) {
return error;
}
if ((error = UART_Print(handle, ".")) != ERROR_NONE) {
return error;
}
if ((error = UART_PrintUIntBase(handle, rightHand,
PRINT_FLOAT_BASE, sigDigits,
false)) != ERROR_NONE) {
return error;
}
return ERROR_NONE;
}
int32_t UART_PrintFloatFiller(
UART *handle,
float value,
unsigned sigDigits,
unsigned width)
{
return UART__PrintFloatFiller(handle, value, sigDigits, width, '0');
}
typedef struct {
unsigned width;
char type;
char filler;
unsigned sigDigits;
int32_t error;
} formatSpec;
static inline formatSpec parseFormatSpecifier(
UART *handle,
char *current,
char **end)
{
formatSpec spec = {.width = 0,
.type = 'd',
.filler = ' ',
.sigDigits = 0,
.error = ERROR_NONE};
if (!handle || !current) {
spec.error = ERROR_PARAMETER;
return spec;
}
bool seenType = false, seenPoint = false;
unsigned temp = 0;
while (!seenType) {
if ((*current >= '0') && (*current <= '9')) {
if ((temp == 0) && (*current == '0')) {
// filler spec
spec.filler = '0';
}
else {
temp *= 10;
temp += (*current) - '0';
}
}
else if (*current == '.') {
spec.width = temp;
temp = 0;
seenPoint = true;
}
else if ((*current >= 'a') || (*current <= 'z')) {
if (*current == 'l') {
// ignore long types
current++;
continue;
}
spec.type = *current;
if (seenPoint) {
spec.sigDigits = temp;
}
else {
spec.width = temp;
}
seenType = true;
*end = current;
}
else {
spec.error = ERROR_UART_PRINTF_INVALID;
return spec;
}
current++;
}
return spec;
}
int32_t UART_vPrintf(UART *handle, const char *format, va_list args)
{
if (!handle) {
return ERROR_PARAMETER;
}
char tempBuffer[PRINT_TEMP_PRINTF_BUFFER] = {'\0'};
unsigned tempIndex = 0;
int32_t error = ERROR_NONE;
/* Loop through string and look for format specifier */
char *start = NULL, *end = NULL;
formatSpec spec;
char c;
while (*format != '\0') {
if (tempIndex >= (PRINT_TEMP_PRINTF_BUFFER - 1)) {
error = ERROR_UART_PRINTF_INVALID;
break;
}
if (*format == '%') {
start = (char*)(format + 1);
if (*start == '%') {
// handle %% pseudo-char
tempBuffer[tempIndex++] = *format;
format += 2;
continue;
}
spec = parseFormatSpecifier(
handle, start, &end);
if (spec.error != ERROR_NONE) {
error = spec.error;
break;
}
format = end + 1;
if (tempIndex > 0) {
/* Write previously globbed tempBuffer */
tempBuffer[tempIndex] = '\0';
tempIndex = 0;
if ((error = UART_Print(
handle, (const char*)tempBuffer)) != ERROR_NONE) {
break;
}
}
/* Write formatted arg */
switch (spec.type) {
case 'd':
case 'i':
error = UART__PrintIntBaseFiller(
handle, va_arg(args, int), 10,
spec.width, false, spec.filler);
break;
case 'u':
error = UART__PrintUIntBaseFiller(
handle, va_arg(args, uint32_t), 10,
spec.width, false, spec.filler);
break;
case 'x':
error = UART__PrintUIntBaseFiller(
handle, va_arg(args, uint32_t), 16,
spec.width, false, spec.filler);
break;
case 'o':
error = UART__PrintUIntBaseFiller(
handle, va_arg(args, uint32_t), 8,
spec.width, false, spec.filler);
break;
case 'f':
error = UART__PrintFloatFiller(
handle, (float)(va_arg(args, double)), spec.sigDigits,
spec.width, spec.filler);
break;
case 's':
error = UART_Print(
handle, va_arg(args, const char*));
break;
case 'c':
c = (char)va_arg(args, int);
error = UART_Print(
handle, (const char*)(&c));
break;
}
}
else {
tempBuffer[tempIndex++] = *format;
format++;
}
}
if (error == ERROR_NONE) {
/* Write previously globbed tempBuffer */
tempBuffer[tempIndex] = '\0';
error = UART_Print(handle, (const char*)tempBuffer);
}
return error;
}
int32_t UART_Printf(UART *handle, const char *format, ...)
{
if (!handle) {
return ERROR_PARAMETER;
}
va_list args;
va_start(args, format);
int32_t error = UART_vPrintf(handle, format, args);
va_end(args);
return error;
}