Author Topic: uTasker Replacements for Standard C String functions  (Read 4558 times)

Offline mhoneywill

  • Full Member
  • ***
  • Posts: 173
    • View Profile
uTasker Replacements for Standard C String functions
« on: April 16, 2009, 12:31:59 PM »
Hi Mark,

In the spirit of uTasker I've investigated the use of uStrcpy and fnDebugDec and fnBufferHex to remove the need for sprintf to concatinate strings.

I've searched the forum and the manuals for information but dis not find anything, so I've worked it out (I think).
Code: [Select]

static char cDisplayStr[40];     // not sure if this needs to be static or if pointer passed ??
unsigned int uiDecNumber = 123;
unsigned int uiHexNumber = 0xa5;
char *cPtr;
               
cPtr = uStrcpy(cDisplayStr, "Part1");
cPtr = uStrcpy(--cPtr, "Part2"); // decrement cPtr to loose null at end of string
cPtr--;                                                      // Loose Null from end of string
*cPtr++ = ' '; // Add space character to string
cPtr = fnDebugDec(uiDecNumber, 0, --cPtr); // Add a number in DECIMAL format to string
*cPtr++ = '*'; // Add * character to string
cPtr = fnBufferHex(uiHexNumber, (2 | WITH_LEADIN | NO_TERMINATOR), cPtr); // Add a number in HEX format to string, precede with 0x and display 4 hex digits
*cPtr++ = ':'; // Terminate String with : character

Would be the same as             
Code: [Select]
static char cDisplayStr[40];     // not sure if this needs to be static or if pointer passed ??
unsigned int uiDecNumber = 123;
unsigned int uiHexNumber = 0xa5;

sprintf(cDisplayStr, "Part1Part2 %d*%x:", uiDecNumber, uiHexNumber);

Does this make sense, you have to be careful as some functions add terminating nulls and some don't.

Please let me know if there is an easier way to do the above,

Cheers

Martin


Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3112
    • View Profile
    • uTasker
Re: uTasker Replacements for Standard C String functions
« Reply #1 on: April 16, 2009, 08:00:40 PM »
Hi Martin

I think that there was one pointer decrement too many at one position, this is the code that i successfully tested in the simulator.

Code: [Select]
static char cDisplayStr[40];     // not sure if this needs to be static or if pointer passed ??
unsigned int uiDecNumber = 123;
unsigned int uiHexNumber = 0xa5;
char *cPtr;
               
cPtr = uStrcpy(cDisplayStr, "Part1");
cPtr = uStrcpy(--cPtr, "Part2"); // decrement cPtr to loose null at end of string
cPtr--;                                                      // Loose Null from end of string
*cPtr++ = ' '; // Add space character to string
cPtr = fnDebugDec(uiDecNumber, 0, cPtr); // Add a number in DECIMAL format to string
*cPtr++ = '*'; // Add * character to string
cPtr = fnBufferHex(uiHexNumber, (2 | WITH_LEADIN | NO_TERMINATOR), cPtr); // Add a number in HEX format to string, precede with 0x and display 4 hex digits
*cPtr++ = ':'; // Terminate String with : character

cDisplayStr[] doesn't generally need to be static.

This does however look rather complicated, and the first part is indeed so since it concatenates two known strings and adds a space, which can of course be performed more like in the sprintf reference.

The following is the same but looks a bit better:

Code: [Select]
static char cDisplayStr[40];     // not sure if this needs to be static or if pointer passed ??
unsigned int uiDecNumber = 123;
unsigned int uiHexNumber = 0xa5;
char *cPtr;     
cPtr = uStrcpy(cDisplayStr, "Part1Part2 ");
cPtr = fnDebugDec(uiDecNumber, NO_TERMINATOR, --cPtr); // Add a number in DECIMAL format to string
*cPtr++ = '*'; // Add * character to string
cPtr = fnBufferHex(uiHexNumber, (2 | WITH_LEADIN | NO_TERMINATOR), cPtr); // Add a number in HEX format to string, precede with 0x and display 4 hex digits
*cPtr++ = ':'; // Terminate String with : character


Essentially this is the way to do it but I have the following notes:
1) It is true that fnDebugDec() is not consistent with the pointer return. When it adds a terminator it will return a pointer to it rather than after it, as the others do.
2) I an not that happy with the fact that the pointer is generally one past the null termination (when added) because it means that a decrement is necessary to concatenate strings. I think that I will add a project option to change this behavior (so that it doesn't generally disturb existing use, if not activated).
3) What I miss is a flag to set CR+LF, which I would also like to add.

Since I have been unhappy with the situation for a time I just improved it. There were only two small changes required and the following is the code to produce the output as you want it:

Code: [Select]
static char cDisplayStr[40];     // not sure if this needs to be static or if pointer passed ??
unsigned int uiDecNumber = 123;
unsigned int uiHexNumber = 0xa5;
char *cPtr;       
cPtr = fnDebugDec(uiDecNumber, NO_TERMINATOR, uStrcpy(cDisplayStr, "Part1Part2 ")); // Add a number in DECIMAL format to string
*cPtr++ = '*'; // Add * character to string
cPtr = fnBufferHex(uiHexNumber, (2 | WITH_LEADIN | NO_TERMINATOR), cPtr); // Add a number in HEX format to string, precede with 0x and display 4 hex digits
*cPtr++ = ':'; // Terminate String with : character

It is a bit compressed due to the call of uStrcpy() within the fnDebugDec() but there are no more pointer decrements required. Using NO_TERMINATOR or WITH_TERMINATOR doesn't in fact change the result in any way since the terminators are no longer counted as pointer increments in any subroutines and they are overwritten anyway in your example.

In driver.c I have the following two changes:

1) at the end of uStrcpy()
    #ifdef STRING_OPTIMISATION                                           // {9}
    return (ptrTo - 1);                                                  // return pointer to null termination to simplify concatination
    #else
    return ptrTo;                                                        // return the pointer to the address which would have been written to next
    #endif


2) at the end of fnBufferHex()
    if (iTerminate) {
#ifdef STRING_OPTIMISATION                                               // {9}
        *pBuf = 0;                                                       // return pointer to null termination to simplify concatination
#else
        *pBuf++ = 0;
#endif
    }


I decided to put the optional define STRING_OPTIMISATION in types.h rather than in config.h since it is so fundamental.

There are various uses of these functions in the demo project which mean that there will be a number of application level changes before all is tuned but the option is now ready to bring some improvements if required.

Finally, I added a new flag called WITH_CR_LF which adds optionally adds CR + LF to the end of a string. This won't need a configuration define since it is compatible but helps the following type of (for me very often used) construction:

fnDebugMsg("Result =");
fnDebugHex(ulValue, (WITH_LEADIN | CODE_CAPITALS | WITH_SPACE | sizeof(ulValue)));
fnDebugMsg("\r\n");


This now can be achieved with

fnDebugMsg("Result =");
fnDebugHex(ulValue, (WITH_LEADIN | CODE_CAPITALS | WITH_SPACE | WITH_CR_LF | sizeof(ulValue)));


This will be in the next SPs...

Finally I do like the fact that pointers are returned (which is not the case with a standard strcpy()) because it allows the complete of strings to be calculated by (cPtr - cDisplayStr) rather than having to call an additional strlen() to find it.

So finally I cleaned up a little. I hope that you nevertheless find the functions useful. They may not be as convenient as printf() types but avoid any library use and so avoid code bloat which often takes place when linking in these library routines. Thanks for keeping with the spirit of the uTasker!!;-)

Regards

Mark

Offline mhoneywill

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Re: uTasker Replacements for Standard C String functions
« Reply #2 on: April 20, 2009, 05:45:46 PM »
Hi Mark,

I know my example looked a bit silly at the beginning, concatenating two strings and adding a space. I was just demonstrating having to decrement the pointer.

Your modifications improve things and make the use of the string functions less error prone. You are right linking in printf / sprintf etc often link in large chunks of code. I will incorporate them.

I presume defining STRING_OPTIMISATION will break the demo applications and they will need slight reworking.

One thought fnDebugDec() and fnDebugHex() and fnBufferHex() take different parameters, would it be worth renaming fnDebugDec() as fnBufferDec() and making fnDebugDec() work more like fnDebugHex()? It just makes things a bit cleaner, I expected the two debug functions to take the same parameters. The other option is to loose fnBufferHex() and make fnDebugHex() work like fnDebugDec(). Just a thought, I know this would require slight re-working of the demo application, but as you are planning to add STRING_OPTIMISATION this might be the ideal time to do it.

extern CHAR *fnDebugDec(signed long slNumberToConvert, unsigned char ucStyle, CHAR *ptrBuf)
extern CHAR *fnBufferHex(unsigned long ulValue, unsigned char ucLen, CHAR *pBuf)
extern void fnDebugHex(unsigned long ulValue, unsigned char uLen)        // display hex value

In your final example, you reduced the code to

Code: [Select]
char cDisplayStr[40];     
unsigned int uiDecNumber = 123;
unsigned int uiHexNumber = 0xa5;
char *cPtr;       
cPtr = fnDebugDec(uiDecNumber, NO_TERMINATOR, uStrcpy(cDisplayStr, "Part1Part2 ")); // Add a number in DECIMAL format to string
*cPtr++ = '*'; // Add * character to string
cPtr = fnBufferHex(uiHexNumber, (2 | WITH_LEADIN | NO_TERMINATOR), cPtr); // Add a number in HEX format to string, precede with 0x and display 4 hex digits
*cPtr++ = ':';

Would you not need to add an extra *cPtr++ = 0; to the end to ensure that the string is null terminated?

Cheers

Martin

Offline mark

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 3112
    • View Profile
    • uTasker
Re: uTasker Replacements for Standard C String functions
« Reply #3 on: April 20, 2009, 07:07:02 PM »
Hi Martin

Yes, the change with STRING_OPTIMISATION causes the demo to break slightly so probably needs a few #ifdefs to keep compatibilty with and without. I will look into changing also the use of the hex and dec routines to be dependent on the STRING_OPTIMISATION too. This should enable a fairly painless transition in the next versions.

Also the string in my example is not terminated - I was thinking that you wanted the ':' as terminator, but thinking about it the sprintf would have terminated it. In the example it would be correct to add the null manually, or else with a uStrcpy(cPtr, ":").

Regards

Mark

PS. The use of sprintf() is of course not prohibited. This can be taken from the standard library (as available with every compiler) so it is up to the user whether the library replacement functions are used or not. These routines are listed, and partly documented in the function descriptions at http://www.utasker.com/docs/Code.html under "Operating System -> Library Replacement"