mini-printf.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  1. /*
  2. * The Minimal snprintf() implementation
  3. *
  4. * Copyright (c) 2013,2014 Michal Ludvig <michal@logix.cz>
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * * Neither the name of the auhor nor the names of its contributors
  15. * may be used to endorse or promote products derived from this software
  16. * without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. * ----
  30. *
  31. * This is a minimal snprintf() implementation optimised
  32. * for embedded systems with a very limited program memory.
  33. * mini_snprintf() doesn't support _all_ the formatting
  34. * the glibc does but on the other hand is a lot smaller.
  35. * Here are some numbers from my STM32 project (.bin file size):
  36. * no snprintf(): 10768 bytes
  37. * mini snprintf(): 11420 bytes (+ 652 bytes)
  38. * glibc snprintf(): 34860 bytes (+24092 bytes)
  39. * Wasting nearly 24kB of memory just for snprintf() on
  40. * a chip with 32kB flash is crazy. Use mini_snprintf() instead.
  41. *
  42. */
  43. #include "mini-printf.h"
  44. static unsigned int
  45. mini_strlen(const char *s)
  46. {
  47. unsigned int len = 0;
  48. while (s[len] != '\0') len++;
  49. return len;
  50. }
  51. static unsigned int
  52. mini_itoa(int value, unsigned int radix, unsigned int uppercase, unsigned int unsig,
  53. char *buffer, unsigned int zero_pad)
  54. {
  55. char *pbuffer = buffer;
  56. int negative = 0;
  57. unsigned int i, len;
  58. /* No support for unusual radixes. */
  59. if (radix > 16)
  60. return 0;
  61. if (value < 0 && !unsig) {
  62. negative = 1;
  63. value = -value;
  64. }
  65. /* This builds the string back to front ... */
  66. do {
  67. int digit = value % radix;
  68. *(pbuffer++) = (digit < 10 ? '0' + digit : (uppercase ? 'A' : 'a') + digit - 10);
  69. value /= radix;
  70. } while (value > 0);
  71. for (i = (pbuffer - buffer); i < zero_pad; i++)
  72. *(pbuffer++) = '0';
  73. if (negative)
  74. *(pbuffer++) = '-';
  75. *(pbuffer) = '\0';
  76. /* ... now we reverse it (could do it recursively but will
  77. * conserve the stack space) */
  78. len = (pbuffer - buffer);
  79. for (i = 0; i < len / 2; i++) {
  80. char j = buffer[i];
  81. buffer[i] = buffer[len-i-1];
  82. buffer[len-i-1] = j;
  83. }
  84. return len;
  85. }
  86. struct mini_buff {
  87. char *buffer, *pbuffer;
  88. unsigned int buffer_len;
  89. };
  90. static int
  91. _putc(int ch, struct mini_buff *b)
  92. {
  93. if ((unsigned int)((b->pbuffer - b->buffer) + 1) >= b->buffer_len)
  94. return 0;
  95. *(b->pbuffer++) = ch;
  96. *(b->pbuffer) = '\0';
  97. return 1;
  98. }
  99. static int
  100. _puts(char *s, unsigned int len, struct mini_buff *b)
  101. {
  102. unsigned int i;
  103. if (b->buffer_len - (b->pbuffer - b->buffer) - 1 < len)
  104. len = b->buffer_len - (b->pbuffer - b->buffer) - 1;
  105. /* Copy to buffer */
  106. for (i = 0; i < len; i++)
  107. *(b->pbuffer++) = s[i];
  108. *(b->pbuffer) = '\0';
  109. return len;
  110. }
  111. int
  112. mini_vsnprintf(char *buffer, unsigned int buffer_len, const char *fmt, va_list va)
  113. {
  114. struct mini_buff b;
  115. char bf[24];
  116. char ch;
  117. b.buffer = buffer;
  118. b.pbuffer = buffer;
  119. b.buffer_len = buffer_len;
  120. while ((ch=*(fmt++))) {
  121. if ((unsigned int)((b.pbuffer - b.buffer) + 1) >= b.buffer_len)
  122. break;
  123. if (ch!='%')
  124. _putc(ch, &b);
  125. else {
  126. char zero_pad = 0;
  127. char *ptr;
  128. unsigned int len;
  129. ch=*(fmt++);
  130. /* Zero padding requested */
  131. if (ch=='0') {
  132. ch=*(fmt++);
  133. if (ch == '\0')
  134. goto end;
  135. if (ch >= '0' && ch <= '9')
  136. zero_pad = ch - '0';
  137. ch=*(fmt++);
  138. }
  139. switch (ch) {
  140. case 0:
  141. goto end;
  142. case 'u':
  143. case 'd':
  144. len = mini_itoa(va_arg(va, unsigned int), 10, 0, (ch=='u'), bf, zero_pad);
  145. _puts(bf, len, &b);
  146. break;
  147. case 'x':
  148. case 'X':
  149. len = mini_itoa(va_arg(va, unsigned int), 16, (ch=='X'), 1, bf, zero_pad);
  150. _puts(bf, len, &b);
  151. break;
  152. case 'c' :
  153. _putc((char)(va_arg(va, int)), &b);
  154. break;
  155. case 's' :
  156. ptr = va_arg(va, char*);
  157. _puts(ptr, mini_strlen(ptr), &b);
  158. break;
  159. default:
  160. _putc(ch, &b);
  161. break;
  162. }
  163. }
  164. }
  165. end:
  166. return b.pbuffer - b.buffer;
  167. }
  168. int
  169. mini_snprintf(char* buffer, unsigned int buffer_len, const char *fmt, ...)
  170. {
  171. int ret;
  172. va_list va;
  173. va_start(va, fmt);
  174. ret = mini_vsnprintf(buffer, buffer_len, fmt, va);
  175. va_end(va);
  176. return ret;
  177. }