Longan Nano
Longan Nano Demo
embedded_string.hpp
Go to the documentation of this file.
1 /**********************************************************************************
2 BSD 3-Clause License
3 
4 Copyright (c) 2020, Orso Eric
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 
10 1. Redistributions of source code must retain the above copyright notice, this
11  list of conditions and the following disclaimer.
12 
13 2. Redistributions in binary form must reproduce the above copyright notice,
14  this list of conditions and the following disclaimer in the documentation
15  and/or other materials provided with the distribution.
16 
17 3. Neither the name of the copyright holder nor the names of its
18  contributors may be used to endorse or promote products derived from
19  this software without specific prior written permission.
20 
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **********************************************************************************/
32 
33 /**********************************************************************************
34 ** ENVIROMENT VARIABILE
35 **********************************************************************************/
36 
37 #ifndef EMBEDDED_STRING_
38  #define EMBEDDED_STRING_
39 
40 /**********************************************************************************
41 ** GLOBAL INCLUDES
42 **********************************************************************************/
43 
44 #include <stdint.h>
45 
46 /**********************************************************************************
47 ** DEFINES
48 **********************************************************************************/
49 
50 //Enable the file trace debugger
51 //#define ENABLE_DEBUG
52 //file trace debugger
53 #ifdef ENABLE_DEBUG
54  #include <cstdio>
55  #include "debug.h"
56 #endif
57 //If DEBUG is not needed, blank out the implementations
58 #ifndef DEBUG_H_
59  #define DEBUG_VARS_PROTOTYPES()
60  #define DEBUG_VARS()
61  #define DSHOW( ... )
62  #define DSTART( ... )
63  #define DSTOP()
64  #define DTAB( ... )
65  #define DPRINT( ... )
66  #define DPRINT_NOTAB( ... )
67  #define DENTER( ... )
68  #define DRETURN( ... )
69  #define DENTER_ARG( ... )
70  #define DRETURN_ARG( ... )
71 #endif
72 
73 /**********************************************************************************
74 ** MACROS
75 **********************************************************************************/
76 
77 /**********************************************************************************
78 ** NAMESPACE
79 **********************************************************************************/
80 
82 namespace User
83 {
84 
85 /**********************************************************************************
86 ** TYPEDEFS
87 **********************************************************************************/
88 
89 /**********************************************************************************
90 ** PROTOTYPE: STRUCTURES
91 **********************************************************************************/
92 
93 /**********************************************************************************
94 ** PROTOTYPE: GLOBAL VARIABILES
95 **********************************************************************************/
96 
97 /**********************************************************************************
98 ** PROTOTYPE: CLASS
99 **********************************************************************************/
100 
101 /************************************************************************************/
103 /************************************************************************************/
126 /************************************************************************************/
127 
128 class String
129 {
130  //Visible to all
131  public:
132  /*********************************************************************************************************************************************************
133  **********************************************************************************************************************************************************
134  ** CONSTRUCTORS
135  **********************************************************************************************************************************************************
136  *********************************************************************************************************************************************************/
137 
138  /***************************************************************************/
141  /***************************************************************************/
145  /***************************************************************************/
146 
147  String( void )
148  {
149  DENTER(); //Trace Enter
153  DRETURN(); //Trace Return
154  return; //OK
155  } //end constructor: String | void
156 
157  /*********************************************************************************************************************************************************
158  **********************************************************************************************************************************************************
159  ** DESTRUCTORS
160  **********************************************************************************************************************************************************
161  *********************************************************************************************************************************************************/
162 
163  /***************************************************************************/
166  /***************************************************************************/
167  // @param
171  /***************************************************************************/
172 
173  ~String( void )
174  {
175  DENTER(); //Trace Enter
179  DRETURN(); //Trace Return
180  return; //OK
181  } //end destructor: String | void
182 
183  /*********************************************************************************************************************************************************
184  **********************************************************************************************************************************************************
185  ** PUBLIC ENUM
186  **********************************************************************************************************************************************************
187  *********************************************************************************************************************************************************/
188 
190  typedef enum _Config
191  {
192  //Safety checks. Can be disabled for performance in production
193  SAFETY_CHECKS = true,
194  //Maximum digits for the various signed and unsigned types
195  DIGIT8 = 3,
196  DIGIT16 = 5,
197  DIGIT32 = 10,
198  //DIGIT64 = 20,
199  //Engineering format has four meaningful digits
201  //Size of special characters
202  STRING_SIGN_SIZE = 1,
204  //Special characters
205  TERMINATOR = '\0',
206  //Maximum size of strings of various types
213  //STRING_SIZE_U64 = DIGIT64 +STRING_TERMINATOR_SIZE,
214  //STRING_SIZE_S64 = DIGIT64 +STRING_SIGN_SIZE +STRING_TERMINATOR_SIZE,
215  //Size of an enginnering format string
217  STRING_SIZE_SENG = 8,
219 
220  /*********************************************************************************************************************************************************
221  **********************************************************************************************************************************************************
222  ** PUBLIC STATIC METHODS
223  **********************************************************************************************************************************************************
224  *********************************************************************************************************************************************************/
225 
226  /***************************************************************************/
229  /***************************************************************************/
236  /***************************************************************************/
237 
238  static uint8_t num_to_str( uint8_t num, uint8_t str_len, char *str )
239  {
240  DENTER_ARG("U8 num: %d\n", num);
241  //----------------------------------------------------------------
242  // CHECKS
243  //----------------------------------------------------------------
244 
245  //If: string is invalid
246  if ((SAFETY_CHECKS == true) && ((str == nullptr) || (str_len == 0)))
247  {
248  //Bad parameters
249  return 0;
250  }
251 
252  //----------------------------------------------------------------
253  // VARS
254  //----------------------------------------------------------------
255 
256  //decimal base
257  const uint8_t base[Config::DIGIT8] =
258  {
259  100,
260  10,
261  1
262  };
263  //Fast counter
264  uint8_t t, tmp;
265  //index to the string
266  uint8_t index = 0;
267  //flag used to blank non meaningful zeros
268  bool flag = true;
269 
270  //----------------------------------------------------------------
271  // BODY
272  //----------------------------------------------------------------
273 
274  //For all bases
275  for (t = 0;t < Config::DIGIT8; t++)
276  {
277  //If the base is bigger or equal than the number (division is meaningful)
278  if (base[t] <= num)
279  {
280  //Divide number by base, get the digit. Bounded to 0 to 9.
281  tmp = num / base[t];
282  //If: index would overflow. Need an extra slot for the final teminator
283  if ( (SAFETY_CHECKS == true) && (index >= (str_len -Config::STRING_TERMINATOR_SIZE)) )
284  {
285  //Put a terminator in first position for safety
286  str[0] = Config::TERMINATOR;
287  //Overflow occurred. Return 0 digits written
288  DRETURN_ARG("ERR: Not enough space: %d\n", str_len);
289  return 0;
290  }
291  //Write the digit
292  str[ index ] = '0' +tmp;
293  //Update the number
294  num = num - base[t] * tmp;
295  //I have found a meaningful digit
296  flag = false;
297  //Jump to the next digit
298  index++;
299  }
300  //If: The base is smaller then the number, and I have yet to find a non zero digit, and I'm not to the last digit
301  else if ( (flag == true) && (t != (Config::DIGIT8 -1)) )
302  {
303  //do nothing
304  }
305  //If: I have a meaningful zero
306  else
307  {
308  //If: index would overflow. Need an extra slot for the final teminator
309  if ( (SAFETY_CHECKS == true) && (index >= (str_len -Config::STRING_TERMINATOR_SIZE)) )
310  {
311  //Put a terminator in first position for safety
312  str[0] = Config::TERMINATOR;
313  //Overflow occurred. Return 0 digits written
314  DRETURN_ARG("ERR: Not enough space: %d\n", str_len);
315  return 0;
316  }
317  //It's a zero
318  str[ index ] = '0';
319  //Jump to the next digit
320  index++;
321  }
322  } //End for: all bases
323 
324  //----------------------------------------------------------------
325  // RETURN
326  //----------------------------------------------------------------
327 
328  //Append the terminator
329  str[ index ] = Config::TERMINATOR;
330  DRETURN_ARG("digits written: %d | output >%s<\n", index, str);
331  return index;
332  } //End public static method: num_to_str | uint8_t | uint8_t | char * |
333 
334  /***************************************************************************/
337  /***************************************************************************/
344  /***************************************************************************/
345 
346  static uint8_t num_to_str( int8_t num, uint8_t str_len, char *str )
347  {
348  DENTER_ARG("S8 num: %d\n", num);
349  //----------------------------------------------------------------
350  // VARS
351  //----------------------------------------------------------------
352 
353  //number of character written
354  uint8_t ret;
355  uint8_t positive;
356 
357  //----------------------------------------------------------------
358  // INIT
359  //----------------------------------------------------------------
360 
361  //If: string is invalid
362  if ((SAFETY_CHECKS == true) && ((str == nullptr) || (str_len == 0)))
363  {
364  //Bad parameters
365  DRETURN_ARG("ERR: bad parameters\n");
366  return 0;
367  }
368 
369  //----------------------------------------------------------------
370  // BODY
371  //----------------------------------------------------------------
372 
373  //If: negative
374  if (num < 0)
375  {
376  //Write sign '-'
377  str[ 0 ] = '-';
378  //Correct sign
379  positive = -num;
380 
381  }
382  //If: zero or positive
383  else
384  {
385  //Write sign '+'
386  str[ 0 ] = '+';
387  positive = num;
388  }
389  //launch the num_to_str to the corrected num, but feed the vector shifted by 1 to make room for the sign. save the return value
390  ret = num_to_str( (uint8_t)positive, str_len -Config::STRING_SIGN_SIZE, &str[Config::STRING_SIGN_SIZE] );
391  //If: no digits were written
392  if (ret == 0)
393  {
394  //Add a terminator for safety
395  str[0] = '\0';
396  DRETURN_ARG("ERR: no digits were written\n");
397  return 0; //No point in adding a sign to an empty string: fail
398  }
399 
400  //----------------------------------------------------------------
401  // RETURN
402  //----------------------------------------------------------------
403  DRETURN_ARG("digits written: %d | output >%s<\n", ret +Config::STRING_SIGN_SIZE, str);
404  return (ret +Config::STRING_SIGN_SIZE);
405  } //End public static method: num_to_str | int8_t | uint8_t | const char * |
406 
407  /***************************************************************************/
410  /***************************************************************************/
417  /***************************************************************************/
418 
419  static uint8_t num_to_str( uint16_t num, uint8_t str_len, char *str )
420  {
421  DENTER_ARG("U16 num: %d\n", num);
422  //----------------------------------------------------------------
423  // CHECKS
424  //----------------------------------------------------------------
425 
426  //If: string is invalid
427  if ((SAFETY_CHECKS == true) && ((str == nullptr) || (str_len == 0)))
428  {
429  //Bad parameters
430  return 0;
431  }
432  //If: number is small enough
433  if (num <= UINT8_MAX)
434  {
435  //Call the faster U8 method
436  uint8_t ret = num_to_str( (uint8_t)num, str_len, str );
437  DRETURN_ARG("digits written: %d\n", ret);
438  return ret;
439  }
440 
441  //----------------------------------------------------------------
442  // VARS
443  //----------------------------------------------------------------
444 
445  //decimal base
446  const uint16_t base[Config::DIGIT16] =
447  {
448  10000,
449  1000,
450  100,
451  10,
452  1
453  };
454  //Fast counter
455  uint8_t t, tmp;
456  //index to the string
457  uint8_t index = 0;
458  //flag used to blank non meaningful zeros
459  bool flag = true;
460 
461  //----------------------------------------------------------------
462  // BODY
463  //----------------------------------------------------------------
464 
465  //For all bases
466  for (t = 0;t < Config::DIGIT16; t++)
467  {
468  //If the base is bigger or equal than the number (division is meaningful)
469  if (base[t] <= num)
470  {
471  //Divide number by base, get the digit. Bounded to 0 to 9.
472  tmp = num / base[t];
473  //If: index would overflow. Need an extra slot for the final teminator
474  if ( (SAFETY_CHECKS == true) && (index >= (str_len -Config::STRING_TERMINATOR_SIZE)) )
475  {
476  //Put a terminator in first position for safety
477  str[0] = Config::TERMINATOR;
478  //Overflow occurred. Return 0 digits written
479  DRETURN_ARG("ERR: Not enough space: %d\n", str_len);
480  return 0;
481  }
482  //Write the digit
483  str[ index ] = '0' +tmp;
484  //Update the number
485  num = num - base[t] * tmp;
486  //I have found a meaningful digit
487  flag = false;
488  //Jump to the next digit
489  index++;
490  }
491  //If: The base is smaller then the number, and I have yet to find a non zero digit, and I'm not to the last digit
492  else if ( (flag == true) && (t != (Config::DIGIT16 -1)) )
493  {
494  //do nothing
495  }
496  //If: I have a meaningful zero
497  else
498  {
499  //If: index would overflow. Need an extra slot for the final teminator
500  if ( (SAFETY_CHECKS == true) && (index >= (str_len -Config::STRING_TERMINATOR_SIZE)) )
501  {
502  //Put a terminator in first position for safety
503  str[0] = Config::TERMINATOR;
504  //Overflow occurred. Return 0 digits written
505  DRETURN_ARG("ERR: Not enough space: %d\n", str_len);
506  return 0;
507  }
508  //It's a zero
509  str[ index ] = '0';
510  //Jump to the next digit
511  index++;
512  }
513  } //End for: all bases
514 
515  //----------------------------------------------------------------
516  // RETURN
517  //----------------------------------------------------------------
518 
519  //Append the terminator
520  str[ index ] = Config::TERMINATOR;
521  DRETURN_ARG("digits written: %d | output >%s<\n", index, str);
522  return index;
523  } //End public static method: num_to_str | uint16_t | uint8_t | char * |
524 
525  /***************************************************************************/
528  /***************************************************************************/
536  /***************************************************************************/
537 
538  static uint8_t num_to_str( int16_t num, uint8_t str_len, char *str )
539  {
540  DENTER_ARG("S16 num: %d\n", num);
541  //----------------------------------------------------------------
542  // VARS
543  //----------------------------------------------------------------
544 
545  //number of character written
546  uint8_t ret;
547  uint16_t positive;
548 
549  //----------------------------------------------------------------
550  // INIT
551  //----------------------------------------------------------------
552 
553  //If: string is invalid
554  if ((SAFETY_CHECKS == true) && ((str == nullptr) || (str_len == 0)))
555  {
556  //Bad parameters
557  DRETURN_ARG("ERR: bad parameters\n");
558  return 0;
559  }
560 
561  //----------------------------------------------------------------
562  // BODY
563  //----------------------------------------------------------------
564 
565  //If: negative
566  if (num < 0)
567  {
568  //Write sign '-'
569  str[ 0 ] = '-';
570  //Correct sign
571  positive = -num;
572 
573  }
574  //If: zero or positive
575  else
576  {
577  //Write sign '+'
578  str[ 0 ] = '+';
579  positive = num;
580  }
581 
582  //If: number is small enough. Note that -255 doesn't fit 8bit but can still be converted by U8 num_to_str because the numeric part does fit 8bit
583  if (positive < UINT8_MAX)
584  {
585  //launch the U8 num_to_str to the corrected num, but feed the vector shifted by 1 to make room for the sign. save the return value
586  ret = num_to_str( (uint8_t)positive, str_len -Config::STRING_SIGN_SIZE, &str[Config::STRING_SIGN_SIZE] );
587  }
588  else
589  {
590  //Full sized num_to_str
591  ret = num_to_str( (uint16_t)positive, str_len -Config::STRING_SIGN_SIZE, &str[Config::STRING_SIGN_SIZE] );
592  }
593  //If: no digits were written
594  if (ret == 0)
595  {
596  //Add a terminator for safety
597  str[0] = '\0';
598  DRETURN_ARG("ERR: no digits were written\n");
599  return 0; //No point in adding a sign to an empty string: fail
600  }
601 
602  //----------------------------------------------------------------
603  // RETURN
604  //----------------------------------------------------------------
605  DRETURN_ARG("digits written: %d | output >%s<\n", ret +Config::STRING_SIGN_SIZE, str);
606  return (ret +Config::STRING_SIGN_SIZE);
607  } //End public static method: num_to_str | int16_t | uint8_t | char * |
608 
609  /***************************************************************************/
612  /***************************************************************************/
619  /***************************************************************************/
620 
621  static uint8_t num_to_str( uint32_t num, uint8_t str_len, char *str )
622  {
623  DENTER_ARG("U32 num: %ld\n", (uint64_t)num);
624  //----------------------------------------------------------------
625  // CHECKS
626  //----------------------------------------------------------------
627 
628  //If: string is invalid
629  if ((SAFETY_CHECKS == true) && ((str == nullptr) || (str_len == 0)))
630  {
631  //Bad parameters
632  return 0;
633  }
634  //If: number is small enough for U8
635  if (num <= UINT8_MAX)
636  {
637  //Call the faster U8 method
638  uint8_t ret = num_to_str( (uint8_t)num, str_len, str );
639  DRETURN_ARG("digits written: %d\n", ret);
640  return ret;
641  }
642  //If: number is small enough for U16
643  else if (num <= UINT16_MAX)
644  {
645  //Call the faster 16 method
646  uint8_t ret = num_to_str( (uint16_t)num, str_len, str );
647  DRETURN_ARG("digits written: %d\n", ret);
648  return ret;
649  }
650 
651  //----------------------------------------------------------------
652  // VARS
653  //----------------------------------------------------------------
654 
655  //decimal base
656  const uint32_t base[Config::DIGIT32] =
657  {
658  1000000000,
659  100000000,
660  10000000,
661  1000000,
662  100000,
663  10000,
664  1000,
665  100,
666  10,
667  1
668  };
669  //Fast counter
670  uint8_t t, tmp;
671  //index to the string
672  uint8_t index = 0;
673  //flag used to blank non meaningful zeros
674  bool flag = true;
675 
676  //----------------------------------------------------------------
677  // BODY
678  //----------------------------------------------------------------
679 
680  //For all bases
681  for (t = 0;t < Config::DIGIT32; t++)
682  {
683  //If the base is bigger or equal than the number (division is meaningful)
684  if (base[t] <= num)
685  {
686  //Divide number by base, get the digit. Bounded to 0 to 9.
687  tmp = num / base[t];
688  //If: index would overflow. Need an extra slot for the final teminator
689  if ( (SAFETY_CHECKS == true) && (index >= (str_len -Config::STRING_TERMINATOR_SIZE)) )
690  {
691  //Put a terminator in first position for safety
692  str[0] = Config::TERMINATOR;
693  //Overflow occurred. Return 0 digits written
694  DRETURN_ARG("ERR: Not enough space: %d\n", str_len);
695  return 0;
696  }
697  //Write the digit
698  str[ index ] = '0' +tmp;
699  //Update the number
700  num = num - base[t] * tmp;
701  //I have found a meaningful digit
702  flag = false;
703  //Jump to the next digit
704  index++;
705  }
706  //If: The base is smaller then the number, and I have yet to find a non zero digit, and I'm not to the last digit
707  else if ( (flag == true) && (t != (Config::DIGIT32 -1)) )
708  {
709  //do nothing
710  }
711  //If: I have a meaningful zero
712  else
713  {
714  //If: index would overflow. Need an extra slot for the final teminator
715  if ( (SAFETY_CHECKS == true) && (index >= (str_len -Config::STRING_TERMINATOR_SIZE)) )
716  {
717  //Put a terminator in first position for safety
718  str[0] = Config::TERMINATOR;
719  //Overflow occurred. Return 0 digits written
720  DRETURN_ARG("ERR: Not enough space: %d\n", str_len);
721  return 0;
722  }
723  //It's a zero
724  str[ index ] = '0';
725  //Jump to the next digit
726  index++;
727  }
728  } //End for: all bases
729 
730  //----------------------------------------------------------------
731  // RETURN
732  //----------------------------------------------------------------
733 
734  //Append the terminator
735  str[ index ] = Config::TERMINATOR;
736  DRETURN_ARG("digits written: %d | output >%s<\n", index, str);
737  return index;
738  } //End public static method: num_to_str | uint32_t | const char * |
739 
740  /***************************************************************************/
743  /***************************************************************************/
750  /***************************************************************************/
751 
752  static uint8_t num_to_str( int32_t num, uint8_t str_len, char *str )
753  {
754  DENTER_ARG("S32 num: %d\n", num);
755  //----------------------------------------------------------------
756  // VARS
757  //----------------------------------------------------------------
758 
759  //number of character written
760  uint8_t ret;
761  uint32_t positive;
762 
763  //----------------------------------------------------------------
764  // INIT
765  //----------------------------------------------------------------
766 
767  //If: string is invalid
768  if ((SAFETY_CHECKS == true) && ((str == nullptr) || (str_len == 0)))
769  {
770  //Bad parameters
771  DRETURN_ARG("ERR: bad parameters\n");
772  return 0;
773  }
774 
775  //----------------------------------------------------------------
776  // BODY
777  //----------------------------------------------------------------
778 
779  //If: negative
780  if (num < 0)
781  {
782  //Write sign '-'
783  str[ 0 ] = '-';
784  //Correct sign
785  positive = -num;
786 
787  }
788  //If: zero or positive
789  else
790  {
791  //Write sign '+'
792  str[ 0 ] = '+';
793  positive = num;
794  }
795 
796  //If: number is small enough. Note that -255 doesn't fit 8bit but can still be converted by U8 num_to_str because the numeric part does fit 8bit
797  if (positive < UINT8_MAX)
798  {
799  //launch the U8 num_to_str to the corrected num, but feed the vector shifted by 1 to make room for the sign. save the return value
800  ret = num_to_str( (uint8_t)positive, str_len -Config::STRING_SIGN_SIZE, &str[Config::STRING_SIGN_SIZE] );
801  }
802  //If small enough
803  else if (positive < UINT16_MAX)
804  {
805  //Faster 16b method
806  ret = num_to_str( (uint16_t)positive, str_len -Config::STRING_SIGN_SIZE, &str[Config::STRING_SIGN_SIZE] );
807  }
808  //If: full size
809  else
810  {
811  //Full sized num_to_str
812  ret = num_to_str( (uint32_t)positive, str_len -Config::STRING_SIGN_SIZE, &str[Config::STRING_SIGN_SIZE] );
813  }
814  //If: no digits were written
815  if (ret == 0)
816  {
817  //Add a terminator for safety
818  str[0] = '\0';
819  DRETURN_ARG("ERR: no digits were written\n");
820  return 0; //No point in adding a sign to an empty string: fail
821  }
822 
823  //----------------------------------------------------------------
824  // RETURN
825  //----------------------------------------------------------------
826  DRETURN_ARG("digits written: %d | output >%s<\n", ret +Config::STRING_SIGN_SIZE, str);
827  return (ret +Config::STRING_SIGN_SIZE);
828  } //End public static method: num_to_str | int32_t | uint8_t | char * |
829 
830  /***************************************************************************/
833  /***************************************************************************/
850  /***************************************************************************/
851 
852  static uint8_t num_to_eng( uint32_t num, int8_t num_exp, uint8_t str_len, char *str )
853  {
854  DENTER_ARG("U32 num: %zu | base exponent 10^%d\n", num, num_exp);
855  //----------------------------------------------------------------
856  // CHECK
857  //----------------------------------------------------------------
858  //If: bad string
859  if ((Config::SAFETY_CHECKS == true) && ((str == nullptr) || (str_len < Config::STRING_SIZE_UENG)) )
860  {
861  DRETURN_ARG("bad input string: %d\n", str_len);
862  return 0;
863  }
864  //If: 0
865  if (num == 0)
866  {
867  //Handle it as special case ssince it would require a change in the algorithm
868  str[0] = '0';
869  str[1] = '.';
870  str[2] = '0';
871  str[3] = '0';
872  str[4] = '0';
873  str[5] = ' ';
874  str[6] = Config::TERMINATOR;
875  return Config::STRING_SIZE_UENG;
876  }
877 
878  //----------------------------------------------------------------
879  // PRECISION DECODER
880  //----------------------------------------------------------------
881  // Get to an integer number with at most four significant digits
882 
883  //How many position was the number shifted
884  int8_t shift = 0;
885 
886  bool f_continue = true;
887  //Number has too many digits
888  while (f_continue == true)
889  {
890  //If: more than five digits
891  if (num >= 100000)
892  {
893  //Divide by 10 and take into account digits shifted away
894  num /= 10;
895  shift++;
896 
897  }
898  //If: more than four digits
899  else if (num >= 10000)
900  {
901  //Compute division
902  int16_t num_tmp = num / 10;
903  //I'm shifting away a number bigger than 5
904  if (num %10 >=5)
905  {
906  //Round up
907  num_tmp++;
908  }
909  //Save
910  num = num_tmp;
911  shift++;
912 
913  }
914  //if: fewer than four digits
915  else if (num < 1000)
916  {
917  num *= 10;
918  shift--;
919  }
920  else
921  {
922  f_continue = false;
923  }
924  }
925  //DPRINT("num: %d | shift: %d\n", num, shift);
926  //Take into account the base exponent of the number provided by the user
927  shift = shift +num_exp;
928 
929  //Algorithm to decode point and si suffix from the shift level. Number is always four digits
930  int8_t point;
931  uint8_t si_suffix_index;
932  if (shift < 0)
933  {
934  point = (3- ((-shift-1) %3));
935  si_suffix_index = ((shift+1)/3) +6;
936  }
937  else
938  {
939  point = 1+ ((shift) %3);
940  si_suffix_index = (shift+3)/3 +6;
941  }
942  //exponents suffix string
943  const char *suffix = "afpnum KMGTPE";
944  //Construct SI suffix
945  str[5] = suffix[ si_suffix_index ];
946  //DPRINT("shift: %d, point: %d | si exp: %d\n", shift, point, si_suffix_index);
947 
948  //----------------------------------------------------------------
949  // NUMERIC STRING
950  //----------------------------------------------------------------
951 
952  //decimal base
953  const uint16_t base[Config::DIGIT_ENG] =
954  {
955  1000,
956  100,
957  10,
958  1
959  };
960  //index to the output string
961  uint8_t index = 0;
962  uint8_t tmp;
963  //For all bases
964  for (uint8_t t = 0;t < Config::DIGIT_ENG; t++)
965  {
966  //If the base is bigger or equal than the number (division is meaningful)
967  if (base[t] <= num)
968  {
969  //Divide number by base, get the digit. Bounded to 0 to 9.
970  tmp = num / base[t];
971  //Write the digit
972  str[ index ] = '0' +tmp;
973  //Update the number
974  num = num - base[t] * tmp;
975  //Jump to the next digit
976  index++;
977  //If index is occupied by the point
978  if (index == point)
979  {
980  //Place the point
981  str[index] = '.';
982  //Jump to the next digit
983  index++;
984  }
985  }
986  //If: I have a meaningful zero
987  else
988  {
989  //It's a zero
990  str[ index ] = '0';
991  //Jump to the next digit
992  index++;
993  //If index is occupied by the point
994  if (index == point)
995  {
996  //Place the point
997  str[index] = '.';
998  //Jump to the next digit
999  index++;
1000  }
1001  }
1002  } //End for: all bases
1003  index = 6;
1004  //Terminator
1005  str[index] = Config::TERMINATOR;
1006 
1007  //----------------------------------------------------------------
1008  // RETURN
1009  //----------------------------------------------------------------
1010  DRETURN_ARG("digits written: %d | output >%s<\n", index, str);
1011  return (index);
1012  } //End public static method: num_to_str | int32_t | uint8_t | char * |
1013 
1014  /***************************************************************************/
1017  /***************************************************************************/
1025  /***************************************************************************/
1026 
1027  static uint8_t num_to_eng( int32_t num, int8_t num_exp, uint8_t str_len, char *str )
1028  {
1029  DENTER_ARG("S32 num: %ld | base exponent 10^%d\n", (int32_t)num, num_exp);
1030  //----------------------------------------------------------------
1031  // VARS
1032  //----------------------------------------------------------------
1033 
1034  //number of character written
1035  uint8_t ret;
1036  uint32_t positive;
1037 
1038  //----------------------------------------------------------------
1039  // INIT
1040  //----------------------------------------------------------------
1041 
1042  //If: string is invalid
1043  if ((SAFETY_CHECKS == true) && ((str == nullptr) || (str_len == 0)))
1044  {
1045  //Bad parameters
1046  DRETURN_ARG("ERR: bad parameters\n");
1047  return 0;
1048  }
1049 
1050  //----------------------------------------------------------------
1051  // BODY
1052  //----------------------------------------------------------------
1053 
1054  //If: negative
1055  if (num < 0)
1056  {
1057  //Write sign '-'
1058  str[ 0 ] = '-';
1059  //Correct sign
1060  positive = -num;
1061 
1062  }
1063  //If: zero or positive
1064  else
1065  {
1066  //Write sign '+'
1067  str[ 0 ] = '+';
1068  positive = num;
1069  }
1070  //Full sized num_to_str
1071  ret = num_to_eng( (uint32_t)positive, num_exp, str_len -Config::STRING_SIGN_SIZE, &str[Config::STRING_SIGN_SIZE] );
1072  //If: no digits were written
1073  if (ret == 0)
1074  {
1075  //Add a terminator for safety
1076  str[0] = '\0';
1077  DRETURN_ARG("ERR: no digits were written\n");
1078  return 0; //No point in adding a sign to an empty string: fail
1079  }
1080 
1081  //----------------------------------------------------------------
1082  // RETURN
1083  //----------------------------------------------------------------
1084  DRETURN_ARG("digits written: %d | output >%s<\n", ret +Config::STRING_SIGN_SIZE, str);
1085  return (ret +Config::STRING_SIGN_SIZE);
1086  } //End public static method: num_to_eng | int32_t | uint8_t | char * |
1087 
1088  /***************************************************************************/
1091  /***************************************************************************/
1098  /***************************************************************************/
1099 
1100  static inline uint8_t num_to_eng( uint32_t num, uint8_t str_len, char *str )
1101  {
1102  DENTER_ARG("S32 num: %d\n", num);
1103 
1104  //----------------------------------------------------------------
1105  // BODY
1106  //----------------------------------------------------------------
1107 
1108  //Wrapper around the conversion function that takes an optional 10^x exponent of the number
1109  uint8_t ret = num_to_eng( num, 0, str_len, str );
1110 
1111  //----------------------------------------------------------------
1112  // RETURN
1113  //----------------------------------------------------------------
1114  DRETURN_ARG("digits written: %d | output >%s<\n", ret, str);
1115  return (ret);
1116  } //End public static method: num_to_eng | int32_t | uint8_t | char * |
1117 
1118  /***************************************************************************/
1121  /***************************************************************************/
1128  /***************************************************************************/
1129 
1130  static inline uint8_t num_to_eng( int32_t num, uint8_t str_len, char *str )
1131  {
1132  DENTER_ARG("S32 num: %d\n", num);
1133 
1134  //----------------------------------------------------------------
1135  // BODY
1136  //----------------------------------------------------------------
1137 
1138  //Wrapper around the conversion function that takes an optional 10^x exponent of the number
1139  uint8_t ret = num_to_eng( num, 0, str_len, str );
1140 
1141  //----------------------------------------------------------------
1142  // RETURN
1143  //----------------------------------------------------------------
1144  DRETURN_ARG("digits written: %d | output >%s<\n", ret, str);
1145  return (ret);
1146  } //End public static method: num_to_eng | int32_t | uint8_t | char * |
1147  //Visible only inside the class
1148  private:
1149  /*********************************************************************************************************************************************************
1150  **********************************************************************************************************************************************************
1151  ** PRIVATE METHODS
1152  **********************************************************************************************************************************************************
1153  *********************************************************************************************************************************************************/
1154 
1155  /*********************************************************************************************************************************************************
1156  **********************************************************************************************************************************************************
1157  ** PRIVATE VARS
1158  **********************************************************************************************************************************************************
1159  *********************************************************************************************************************************************************/
1160 
1161 }; //End Class: String
1162 
1163 /**********************************************************************************
1164 ** NAMESPACE
1165 **********************************************************************************/
1166 
1167 } //End Namespace: User
1168 
1169 #else
1170  #warning "Multiple inclusion of hader file EMBEDDED_STRING_"
1171 #endif
User::String::num_to_str
static uint8_t num_to_str(uint8_t num, uint8_t str_len, char *str)
public static method num_to_str | uint8_t | uint8_t | char * |
Definition: embedded_string.hpp:238
DENTER_ARG
#define DENTER_ARG(...)
Definition: embedded_string.hpp:69
User::String::DIGIT_ENG
@ DIGIT_ENG
Definition: embedded_string.hpp:204
User::String::num_to_str
static uint8_t num_to_str(int32_t num, uint8_t str_len, char *str)
public static method num_to_str | int32_t | uint8_t | const char * |
Definition: embedded_string.hpp:752
User::String::~String
~String(void)
Destructor String | void.
Definition: embedded_string.hpp:173
User::String::STRING_SIZE_S16
@ STRING_SIZE_S16
Definition: embedded_string.hpp:214
User::String::DIGIT16
@ DIGIT16
Definition: embedded_string.hpp:200
User::String::STRING_SIZE_UENG
@ STRING_SIZE_UENG
Definition: embedded_string.hpp:220
User::String::num_to_eng
static uint8_t num_to_eng(int32_t num, uint8_t str_len, char *str)
public static method num_to_eng | int32_t | uint8_t | const char * |
Definition: embedded_string.hpp:1130
User::String::STRING_TERMINATOR_SIZE
@ STRING_TERMINATOR_SIZE
Definition: embedded_string.hpp:207
User::String::STRING_SIZE_U16
@ STRING_SIZE_U16
Definition: embedded_string.hpp:213
User
User::String::num_to_eng
static uint8_t num_to_eng(uint32_t num, int8_t num_exp, uint8_t str_len, char *str)
public static method num_to_eng | uint32_t | uint8_t | char * |
Definition: embedded_string.hpp:852
User::String::num_to_str
static uint8_t num_to_str(int8_t num, uint8_t str_len, char *str)
public static method num_to_str | int8_t | uint8_t | char * |
Definition: embedded_string.hpp:346
DENTER
#define DENTER(...)
Definition: embedded_string.hpp:67
User::String::Config
enum User::String::_Config Config
Configurations for the String class.
User::String::STRING_SIZE_U32
@ STRING_SIZE_U32
Definition: embedded_string.hpp:215
User::String::STRING_SIZE_S32
@ STRING_SIZE_S32
Definition: embedded_string.hpp:216
User::String::num_to_str
static uint8_t num_to_str(uint32_t num, uint8_t str_len, char *str)
public static method num_to_str | uint32_t | uint8_t | char * |
Definition: embedded_string.hpp:621
User::String::num_to_eng
static uint8_t num_to_eng(int32_t num, int8_t num_exp, uint8_t str_len, char *str)
public static method num_to_eng | int32_t | uint8_t | const char * |
Definition: embedded_string.hpp:1027
DRETURN_ARG
#define DRETURN_ARG(...)
Definition: embedded_string.hpp:70
User::String::TERMINATOR
@ TERMINATOR
Definition: embedded_string.hpp:209
User::String::STRING_SIZE_S8
@ STRING_SIZE_S8
Definition: embedded_string.hpp:212
User::String
String <-> Number conversion using standard c strings to avoid std::string under the hood.
Definition: embedded_string.hpp:129
User::String::SAFETY_CHECKS
@ SAFETY_CHECKS
Definition: embedded_string.hpp:197
User::String::STRING_SIGN_SIZE
@ STRING_SIGN_SIZE
Definition: embedded_string.hpp:206
User::String::num_to_str
static uint8_t num_to_str(int16_t num, uint8_t str_len, char *str)
public static method num_to_str | int16_t | uint8_t | const char * |
Definition: embedded_string.hpp:538
User::String::STRING_SIZE_SENG
@ STRING_SIZE_SENG
Definition: embedded_string.hpp:221
User::String::num_to_str
static uint8_t num_to_str(uint16_t num, uint8_t str_len, char *str)
public static method num_to_str | uint16_t | uint8_t | char * |
Definition: embedded_string.hpp:419
User::String::num_to_eng
static uint8_t num_to_eng(uint32_t num, uint8_t str_len, char *str)
public static method num_to_eng | uint32_t | uint8_t | const char * |
Definition: embedded_string.hpp:1100
User::String::DIGIT8
@ DIGIT8
Definition: embedded_string.hpp:199
User::String::STRING_SIZE_U8
@ STRING_SIZE_U8
Definition: embedded_string.hpp:211
DRETURN
#define DRETURN(...)
Definition: embedded_string.hpp:68
User::String::_Config
_Config
Configurations for the String class.
Definition: embedded_string.hpp:191
User::String::String
String(void)
Constructor String | void.
Definition: embedded_string.hpp:147
User::String::DIGIT32
@ DIGIT32
Definition: embedded_string.hpp:201