Lazuli
Loading...
Searching...
No Matches
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
26STATIC_ASSERT(2 == sizeof(unsigned int),
27 Sizeof_unsigned_int_not_supported_for_printf);
40static char
41GetHexDigit(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
70static uint8_t
71ConvertU16ToHexadecimal(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
101static uint8_t
102ConvertU16ToOctal(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
131static 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
162static void
163OutputBuffer(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
178static void
179OutputReverseBuffer(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 */
190int
191printf(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 int size;
206 int padLength = 0;
207 char padChar = ' ';
208 bool firstDigitPassed = false;
209 bool isNegative = false;
210 bool rightPadded = false;
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:
264 * - Change the size of 'buffer'.
265 * - Add the new sizeof(unsigned int) in the STATIC_ASSERT.
266 * - Perform the same test on sizeof() for hexadecimal and octal
267 * conversions.
268 */
269 }
270 } else if ('x' == *c || 'X' == *c) {
271 size = ConvertU16ToHexadecimal(va_arg(args, unsigned int),
272 buffer,
273 'X' == *c);
274 } else if ('o' == *c) {
275 size = ConvertU16ToOctal(va_arg(args, unsigned int), buffer);
276 } else if ('c' == *c) {
277 buffer[0] = (char)va_arg(args, int);
278 size = 1;
279 } else if ('s' == *c) {
280 padChar = ' '; /* For strings, we always pad with spaces */
281 s = va_arg(args, char*);
282 size = strlen(s);
283 } else {
284 /*
285 * We encountered an unknown character.
286 * TODO: Error.
287 */
288
289 break;
290 }
291
292 /*
293 * If we are here, it means we have a buffer filled with a conversion.
294 * We then must print this buffer with the appropriate padding.
295 */
296
297 total += size;
298
299 if (rightPadded) {
300 /*
301 * The value is to be right-padded, the operations are:
302 * 1) Output the sign, if concerned.
303 * 2) Output the value.
304 * 3) Output the padding, overriding any pad character by a space
305 * character.
306 */
307
308 if (isNegative) {
309 putchar('-');
310 ++total;
311 }
312
313 if ('s' == *c) {
314 OutputBuffer(s, size);
315 } else {
316 OutputReverseBuffer(s, size);
317 }
318
319 total += OutputPadding(padLength, size, ' ', isNegative);
320 } else {
321 /*
322 * The value is to be left-padded, the operations are:
323 * 1) Output the sign, if concerned and only if we pad with zeros.
324 * 2) Output the padding.
325 * 3) Output the sign, if concerned and only if we pad with spaces.
326 * 3) Output the value.
327 */
328
329 if (isNegative && '0' == padChar) {
330 putchar('-');
331 ++total;
332 }
333
334 total += OutputPadding(padLength, size, padChar, isNegative);
335
336 if (isNegative && ' ' == padChar) {
337 putchar('-');
338 ++total;
339 }
340
341 if ('s' == *c) {
342 OutputBuffer(s, size);
343 } else {
344 OutputReverseBuffer(s, size);
345 }
346 }
347
348 break;
349 }
350 } else {
351 ++total;
352 putchar(*c);
353 }
354 }
355
356 va_end(args);
357
358 return total;
359}
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
Basic type definitions and useful macros.
#define NULL
NULL pointer.
Definition common.h:70
#define ABS(A)
Obtain the absolute value of an integer.
Definition common.h:231
#define STATIC_ASSERT(C, M)
Perform an assertion at compile time.
Definition common.h:65
Include appropriate config file.
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
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
static void OutputBuffer(const char *const buffer, const uint8_t size)
Output the content of the buffer.
Definition printf.c:163
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
static void OutputReverseBuffer(const char *const buffer, uint8_t size)
Output the content of the buffer in reverse order.
Definition printf.c:179
int printf(const char *format,...)
Produce unbuffered formatted output to the serial line.
Definition printf.c:191
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
Internal functions needed for printf().
uint8_t Printf_ConvertU16ToDecimal(uint16_t i, char buffer[])
Convert an unsigned 16-bit integer to its ASCII decimal representation.
AVR type and macros definitions for variadic functions.
stdio standard header file.
size_t strlen(const char *s)
Get the length of the string pointed by s.
Definition string.c:20
libc string header.
int putchar(int c)
Transmit a single character on the serial line.
Definition usart.c:40