Lazuli
printf.c
Go to the documentation of this file.
1 /*
2  * SPDX-License-Identifier: GPL-3.0-only
3  * This file is part of Lazuli.
4  */
5 
15 #include <stdarg.h>
16 #include <stdint.h>
17 #include <stdio.h>
18 #include <string.h>
19 
20 #include <Lazuli/common.h>
21 #include <Lazuli/config.h>
22 
23 #include <Lazuli/sys/printf.h>
24 
26 STATIC_ASSERT(2 == sizeof(unsigned int),
27  Sizeof_unsigned_int_not_supported_for_printf);
40 static char
41 GetHexDigit(const uint8_t i, const bool isUpper)
42 {
43  /* We assume that i <= 15 */
44 
45  if (i <= 9) {
46  return '0' + i;
47  }
48 
49  if (isUpper) {
50  return 'A' + i - 10;
51  }
52 
53  return 'a' + i - 10;
54 }
55 
70 static uint8_t
71 ConvertU16ToHexadecimal(uint16_t value, char * const buffer, const bool isUpper)
72 {
73  uint8_t i;
74  const uint8_t numberOfNibbles = sizeof(value) * 2;
75 
76  /* WARNING! This works only with little endian */
77  for (i = 0; i < numberOfNibbles; ++i) {
78  buffer[i] = GetHexDigit(value & 0xfU, isUpper);
79  value >>= 4U;
80 
81  if (0 == value) {
82  break;
83  }
84  }
85 
86  return i + 1;
87 }
88 
101 static uint8_t
102 ConvertU16ToOctal(uint16_t value, char * const buffer)
103 {
104  uint8_t i;
105  const uint8_t numberOfGroups = 6;
106 
107  /* WARNING! This works only with little endian */
108  for (i = 0; i < numberOfGroups; ++i) {
109  buffer[i] = (char)((value & 07U) + '0');
110  value >>= 3U;
111 
112  if (0 == value) {
113  break;
114  }
115  }
116 
117  return i + 1;
118 }
119 
131 static int
133  const uint8_t size,
134  const char padChar,
135  const bool isNegative)
136 {
137  int total = 0;
138 
139  if (padLength > size) {
140  padLength -= size;
141 
142  if (isNegative && padLength > 0) {
143  --padLength;
144  }
145 
146  total = padLength;
147 
148  while (padLength-- > 0) {
149  putchar(padChar);
150  }
151  }
152 
153  return total;
154 }
155 
162 static void
163 OutputBuffer(const char * const buffer, const uint8_t size)
164 {
165  uint8_t i;
166 
167  for (i = 0; i < size; ++i) {
168  putchar(buffer[i]);
169  }
170 }
171 
178 static void
179 OutputReverseBuffer(const char * const buffer, uint8_t size)
180 {
181  while (size-- > 0) {
182  putchar(buffer[size]);
183  }
184 }
185 
186 /*
187  * Warning! The stack usage of this function is important! Reduce the stack
188  * usage to its strict minimum.
189  */
190 int
191 printf(const char *format, ...)
192 {
193  va_list args;
194  int total = 0;
195  const char *c = format;
196 
197  if (NULL == format) {
198  return 0;
199  }
200 
201  va_start(args, format);
202 
203  for (; '\0' != *c; ++c) {
204  if ('%' == *c) {
205  char padChar = ' ';
206  bool firstDigitPassed = false;
207  bool isNegative = false;
208  bool rightPadded = false;
209  uint8_t padLength = 0;
210  uint8_t size;
211  char buffer[6];
212  const char * s = buffer;
213 
214  for (++c; '\0' != *c; ++c) {
215  /*
216  * First, we parse formatting characters.
217  * After a formatting character, we must continue to parse in the
218  * current loop.
219  */
220 
221  /* This test MUST be performed before the next one */
222  if ('0' == *c && !firstDigitPassed) {
223  padChar = '0';
224  firstDigitPassed = true;
225 
226  continue;
227  } else if (*c >= '0' && *c <= '9') {
228  padLength = padLength * 10 + *c - '0';
229  firstDigitPassed = true;
230 
231  continue;
232  } else if ('-' == *c) {
233  rightPadded = true;
234 
235  continue;
236  } else if ('%' == *c) { /*
237  * Then we parse "terminating characters".
238  * After a terminating character, we must exit
239  * the current loop.
240  */
241 
242  ++total;
243  putchar('%');
244 
245  break;
246  } else if ('d' == *c || 'i' == *c || 'u' == *c) {
247  unsigned int absolute;
248 
249  if ('u' == *c) {
250  absolute = va_arg(args, unsigned int);
251  } else {
252  const int value = va_arg(args, int);
253  isNegative = value < 0;
254  absolute = ABS(value);
255  }
256 
257  if (2 == sizeof(unsigned int)) {
258  size = Printf_ConvertU16ToDecimal(absolute, buffer);
259  } else {
260  /*
261  * Add here the support for another sizeof(unsigned int).
262  *
263  * Remember to change the size of 'buffer', and add the new
264  * sizeof(unsigned int) in the STATIC_ASSERT.
265  */
266  }
267  } else if ('x' == *c || 'X' == *c) {
268  size = ConvertU16ToHexadecimal(va_arg(args, unsigned int),
269  buffer,
270  'X' == *c);
271  } else if ('o' == *c) {
272  size = ConvertU16ToOctal(va_arg(args, unsigned int), buffer);
273  } else if ('c' == *c) {
274  buffer[0] = (char)va_arg(args, int);
275  size = 1;
276  } else if ('s' == *c) {
277  padChar = ' '; /* For strings, we always pad with spaces */
278  s = va_arg(args, char*);
279  size = strlen(s);
280  } else {
281  /*
282  * We encountered an unknown character.
283  * TODO: Error.
284  */
285 
286  break;
287  }
288 
289  /*
290  * If we are here, it means we have a buffer filled with a conversion.
291  * We then must print this buffer with the appropriate padding.
292  */
293 
294  total += size;
295 
296  if (rightPadded) {
297  /*
298  * The value is to be right-padded, the operations are:
299  * 1) Output the sign, if concerned.
300  * 2) Output the value.
301  * 3) Output the padding, overriding any pad character by a space
302  * character.
303  */
304 
305  if (isNegative) {
306  putchar('-');
307  ++total;
308  }
309 
310  if ('s' == *c) {
311  OutputBuffer(s, size);
312  } else {
313  OutputReverseBuffer(s, size);
314  }
315 
316  total += OutputPadding(padLength, size, ' ', isNegative);
317  } else {
318  /*
319  * The value is to be left-padded, the operations are:
320  * 1) Output the sign, if concerned and only if we pad with zeros.
321  * 2) Output the padding.
322  * 3) Output the sign, if concerned and only if we pad with spaces.
323  * 3) Output the value.
324  */
325 
326  if (isNegative && '0' == padChar) {
327  putchar('-');
328  ++total;
329  }
330 
331  total += OutputPadding(padLength, size, padChar, isNegative);
332 
333  if (isNegative && ' ' == padChar) {
334  putchar('-');
335  ++total;
336  }
337 
338  if ('s' == *c) {
339  OutputBuffer(s, size);
340  } else {
341  OutputReverseBuffer(s, size);
342  }
343  }
344 
345  break;
346  }
347  } else {
348  ++total;
349  putchar(*c);
350  }
351  }
352 
353  va_end(args);
354 
355  return total;
356 }
#define STATIC_ASSERT(C, M)
Perform an assertion at compile time.
Definition: common.h:63
Internal functions needed for printf().
libc string header.
int putchar(int c)
Transmit a single character on the serial line.
Definition: usart.c:40
#define ABS(A)
Obtain the absolute value of an integer.
Definition: common.h:243
static char GetHexDigit(const uint8_t i, const bool isUpper)
Convert a base 16 unit value to its hexadecimal digit representation.
Definition: printf.c:41
Include appropriate config file.
static void OutputReverseBuffer(const char *const buffer, uint8_t size)
Output the content of the buffer in reverse order.
Definition: printf.c:179
size_t strlen(const char *s)
Get the length of the string pointed by s.
Definition: string.c:20
unsigned int uint16_t
Represents a unsigned integer type with width of exactly 16 bits.
Definition: stdint.h:94
unsigned char uint8_t
Represents a unsigned integer type with width of exactly 8 bits.
Definition: stdint.h:89
static uint8_t ConvertU16ToHexadecimal(uint16_t value, char *const buffer, const bool isUpper)
Convert an unsigned 16-bit integer to its ASCII hexadecimal representation.
Definition: printf.c:71
#define NULL
NULL pointer.
Definition: common.h:68
AVR type and macros definitions for variadic functions.
int printf(const char *format,...)
Produce unbuffered formatted output to the serial line.
Definition: printf.c:191
uint8_t Printf_ConvertU16ToDecimal(uint16_t i, char buffer[])
Convert an unsigned 16-bit integer to its ASCII decimal representation.
stdio standard header file.
static void OutputBuffer(const char *const buffer, const uint8_t size)
Output the content of the buffer.
Definition: printf.c:163
Basic type definitions and useful macros.
static uint8_t ConvertU16ToOctal(uint16_t value, char *const buffer)
Convert an unsigned 16-bit integer to its ASCII octal representation.
Definition: printf.c:102
static int OutputPadding(uint8_t padLength, const uint8_t size, const char padChar, const bool isNegative)
Output the padding, according to the selected options.
Definition: printf.c:132