jeremiah
Joined: 20 Jul 2010 Posts: 1346
|
Poor Man's snprintf() |
Posted: Thu Mar 12, 2015 2:28 pm |
|
|
Below is a quick and dirty way to get a snprintf() method without having to write all the parsing code. It leverages a CCS extension to work. It makes use of macros, so always be aware of the dangers there.
Code: |
/******************************************************************************
* Name: snprintf.h
* Auth: Jeremiah
* Desc: This file contains a poor man's implementation of snprintf() for the
* CCS compiler. It is not meant to be as efficient as a homemade
* snprintf() method. It leverages the putc() substitution in printf(),
* which is a CCS extension.
*
* FUNCTIONS:
* snprintf()
*
* Compiled with PCWHD v5.025
*
* History:
* 03/12/2015 - JHB - Creation
******************************************************************************/
#ifndef SNPRINTF_H
#define SNPRINTF_H
///////////////////////////////////////////////////////////////////////////////
// NAME: snprintf()
// DESC: Composes a string with the same text that would be printed if fmt
// was used on printf, but instead of being printed, the content is
// stored as a string in the buffer pointed by buf (taking max as the
// maximum buffer capacity to fill).
// IN: buf = Pointer to a buffer where the resulting string is stored
// max = Maximum number of bytes to be used in the buffer. The
// generated string has a length of max-1, leaving space for a
// null character. Expects an unsigned int16 > 0.
// fmt = CString that contains a format that follows the same format
// in printf (see printf for details). This must be a constant
// string (not a variable).
// ... = List of parameters used as inputs to the format string.
// OUT: The number of characters that would have been written if max was
// large enough to hold them.
// NOTE: Does not return a negative value for format errors. However the
// CCS compiler reports format errors at compile time.
///////////////////////////////////////////////////////////////////////////////
#define snprintf(buf,max,fmt,...) ( \
snprintf_init(buf,max), \
printf(snprintf_putc,fmt,__VA_ARGS__), \
snprintf_cleanup(), \
g_snprintfNum )
//Macros that use a comma separated list of statements return the final
//statement as if it were a function return value. Also note that
//semicolons are not needed using this method (they are implied)
///////////////////////////////////////////////////////////////////////////////
//****************** Implementation (Do Not Use Directly) *******************//
///////////////////////////////////////////////////////////////////////////////
static unsigned int16 g_snprintfMax; //this will decrement with each character
static unsigned int16 g_snprintfNum; //this will increment with each character
static char * g_snprintfBuf; //holds address of the output buffer
//function passed to printf() ... CCS option only
void snprintf_putc(char c){
//only copy the maximum number of allowed characters
if(g_snprintfMax){
if(--g_snprintfMax){
g_snprintfBuf[g_snprintfNum] = c;
}else{ //last spot in the buffer must be a '\0'
g_snprintfBuf[g_snprintfNum] = '\0'; //append a null
}
}
//update this regardless of max so the snprintf() return value is correct.
g_snprintfNum++;
}
//function used to prepare for a new snprintf() call
void snprintf_init(char *buf, unsigned int16 max){
g_snprintfMax = max;
g_snprintfNum = 0;
g_snprintfBuf = buf;
if(g_snprintfMax){ //only write to it if buffer size non zero
g_snprintfBuf[0] = '\0';
}
}
//function used to finish up buffer with a null if needed.
void snprintf_cleanup(){
//if maximum buffer size was never reached, then null terminate
if(g_snprintfMax){
g_snprintfBuf[g_snprintfNum] = '\0';
}
}
#endif
|
Test Code (supply your own FUSES, etc.):
Code: |
#case
#include "pic.h" //has #device, #FUSES, #use delay(), #use rs232()
#include "snprintf.h"
void main(void){
char buffer[15] = {
'z','z','z','z','z',
'z','z','z','z','z',
'z','z','z','z','\0'
};
unsigned int16 i;
unsigned int16 val=255;
printf("\r\n*********Program Start*********\r\n");
//Test empty input string
val = snprintf(buffer,sizeof(buffer),"");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
//Test being under the max
val = snprintf(buffer,sizeof(buffer),"Hello");
//show remaining buffer just for this one to verify algorithm isn't
//overwriting the entire buffer.
printf("BUFFER(%03u,%03u): %s %s\r\n", sizeof(buffer),
val,
buffer,
&buffer[val+1]);
val = snprintf(buffer,sizeof(buffer),"Hello ");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
val = snprintf(buffer,sizeof(buffer),"Hello 7");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
val = snprintf(buffer,sizeof(buffer),"Hello 78");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
val = snprintf(buffer,sizeof(buffer),"Hello 789");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
val = snprintf(buffer,sizeof(buffer),"Hello 789A");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
val = snprintf(buffer,sizeof(buffer),"Hello 789AB");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
val = snprintf(buffer,sizeof(buffer),"Hello 789ABC");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
val = snprintf(buffer,sizeof(buffer),"Hello 789ABCD");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
val = snprintf(buffer,sizeof(buffer),"Hello 789ABCDE");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
val = snprintf(buffer,sizeof(buffer),"Hello 789ABCDEF");
printf("BUFFER(%03u,%03u): %s\r\n",sizeof(buffer),val,buffer);
//Test exceeding the max. First loop should print out results from
//previous snprintf() call since buffer max is 0
for(i=0; i<=sizeof(buffer); i++){
val = snprintf(buffer,i,"Hello %d ABCDEFGHIJKLMNOP",78);
printf("BUFFER(%03u,%03u): %s\r\n",i,val,buffer);
}
while(TRUE);
}
|
Expected Output:
Code: |
*********Program Start*********
BUFFER(015,000):
BUFFER(015,005): Hello zzzzzzzz
BUFFER(015,006): Hello
BUFFER(015,007): Hello 7
BUFFER(015,008): Hello 78
BUFFER(015,009): Hello 789
BUFFER(015,010): Hello 789A
BUFFER(015,011): Hello 789AB
BUFFER(015,012): Hello 789ABC
BUFFER(015,013): Hello 789ABCD
BUFFER(015,014): Hello 789ABCDE
BUFFER(015,015): Hello 789ABCDE
BUFFER(000,025): Hello 789ABCDE
BUFFER(001,025):
BUFFER(002,025): H
BUFFER(003,025): He
BUFFER(004,025): Hel
BUFFER(005,025): Hell
BUFFER(006,025): Hello
BUFFER(007,025): Hello
BUFFER(008,025): Hello 7
BUFFER(009,025): Hello 78
BUFFER(010,025): Hello 78
BUFFER(011,025): Hello 78 A
BUFFER(012,025): Hello 78 AB
BUFFER(013,025): Hello 78 ABC
BUFFER(014,025): Hello 78 ABCD
BUFFER(015,025): Hello 78 ABCDE
|
|
|