Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.
|
Forum Index : Microcontroller and PC projects : MM2 Playing with CFunctions LTRIM,RTRIM
Page 1 of 2 | |||||
Author | Message | ||||
twofingers Guru Joined: 02/06/2014 Location: GermanyPosts: 1133 |
As a result of my playing with Cfunctions I can present some simple code examples. Each call takes about 0,2ms. Syntax for CFunction header: ltrim(STRING) STRING rtrim(STRING) STRING Dim x$, a$ x$=" Hello World " Print "input string:>"x$;"<",Len(x$) Timer=0 For i = 1 To 1000 a$=rtrim(x$) Next i Print "Time for Rtrim:";Timer/1000;"ms" Print ">";a$;"<",Len(a$) Timer=0 For i = 1 To 1000 a$=ltrim(x$) Next i Print "Time for Ltrim:";Timer/1000;"ms" Print ">";a$;"<",Len(a$) End ' File rtrim.bas written 13-Jul-2017 23:05:06 ' CFunction ltrim(STRING) STRING 00000000 27BDFFE0 AFBF001C AFB10018 AFB00014 00808021 90910000 3C029D00 8C420040 0040F809 26240001 1220001A A0400000 00002021 24060020 24830001 306300FF 0223282B 54A00006 02248823 26100001 82050000 50A6FFF8 00602021 02248823 323100FF 5220000B A0510000 24030001 00432021 92050000 A0850000 24630001 306300FF 0223202B 1080FFF9 26100001 A0510000 8FBF001C 8FB10018 8FB00014 03E00008 27BD0020 End CFunction ' CFunction rtrim(STRING) STRING 00000000 27BDFFE0 AFBF001C AFB10018 AFB00014 00808821 90900000 3C029D00 8C420040 0040F809 26040001 1200001D A0400000 02301821 00002821 10000002 24070020 00802821 24A40001 308400FF 0204302B 54C00005 02058023 80660000 10C7FFF8 2463FFFF 02058023 321000FF 5200000C A0500000 24030001 26310001 00432021 92250000 A0850000 24630001 306300FF 0203202B 1080FFF9 26310001 A0500000 8FBF001C 8FB10018 8FB00014 03E00008 27BD0020 End CFunction C source: //-------------------------------------------- // // LTrim and RTrim (VB style) // requires MMBasic for MX170 V5.02 or later // Syntax for CFunction header: // ltrim(STRING) STRING // rtrim(STRING) STRING // // eg a$=ltrim(" Hello World") // PRINT a$ ----> "Hello World" //-------------------------------------------- // For the TBS-Forum 13.07.2017 //-------------------------------------------- #define _SUPPRESS_PLIB_WARNING #include <plib.h> #include "cfunctions.h" // // strip leading whitespace from a string // char *ltrim(char *string){ unsigned char len = (unsigned char) string[0]; unsigned char i = 0, tr_len; char *s = GetTempMemory(len+1); s[0] = 0; // for empty string if (len > 0) { //find first occurrence of non whitespace char from the left while(++i <= len && *++string == 32); i--; // number of spaces //remaining string length = len - i tr_len = len-i; //copy remaining string to s[] for(i = 1; i <= tr_len; i++) s[i ] = *string++; s[0] = tr_len;// store BASIC (trimmed) string length } return s; } // // strip trailing whitespace from a string // char *rtrim(char *string){ char *tr_string = string; // copy pointer unsigned char len = (unsigned char) string[0]; unsigned char i = 0, tr_len; char *s = GetTempMemory(len+1); s[0] = 0; // for empty string //if we have an empty string, nothing to do if (len != 0) { //find first occurrence of non whitespace char from the right while(++i <= len && *(--string+len+1) == 32); i--; // number of spaces //remaining string length = len - i tr_len = len-i; //copy remaining string to s[] for(i = 1; i <= tr_len; i++) s[i ]= *++tr_string; s[0] = tr_len;// store BASIC (trimmed) string length } return s; } void main(){} to be continued ... Michael edit: The forum software "eats" the [i ] Download:2017-07-14_142016_LTRIM_RTRIM_CFunctions.zip |
||||
led-bloon Senior Member Joined: 21/12/2014 Location: AustraliaPosts: 203 |
What about the TAB character CHR$(09)? Miss you George |
||||
twofingers Guru Joined: 02/06/2014 Location: GermanyPosts: 1133 |
This is only for whitespaces (VB style LTRIM & RTRIM) It would be easy to change the code to eg LTrim(Inputstring$,chr$(9)). But that's not what I intended. It should be rather a example HOW to make such CFunctions. They need MMBasic V5.02 or later! Regards Michael |
||||
Grogster Admin Group Joined: 31/12/2012 Location: New ZealandPosts: 9066 |
Nice one, Michael. We need as many Cfunction/Csub writers as possible, to take the workload off matherp C is a complete mystery to me, and I don't have the time to it, otherwise I would also be learning to write Cfunction/Csub stuff. Smoke makes things work. When the smoke gets out, it stops! |
||||
twofingers Guru Joined: 02/06/2014 Location: GermanyPosts: 1133 |
Thanks! And we need also more examples. CFunctions are fantastic! Kind regards Michael |
||||
paceman Guru Joined: 07/10/2011 Location: AustraliaPosts: 1328 |
Yes, +1 from me too, as Grogster said Michael - nice job! Now where is that library? Greg |
||||
Geoffg Guru Joined: 06/06/2011 Location: AustraliaPosts: 3165 |
Excellent, would it be OH if I included it in the Micromite MMBasic distribution? BTW, you do not have to worry about tab characters. MMBasic automatically converts them to spaces on INPUT, LINE INPUT, program load, etc. Geoff Geoff Graham - http://geoffg.net |
||||
piclover Senior Member Joined: 14/06/2015 Location: FrancePosts: 134 |
There are obvious optimizations to make, the first two being of great benefit: - You should test for the case when there is nothing for ltrim() and rtrim() to do, and return 'string' itself in these cases (thus sparing a memory reservation and string copy). - When there is something to do (spaces to remove), you should reserve memory for the final string only after having calculated the amount of bytes actually needed (thus using less memory during the function call). - You could avoid entirely the use of 'tr_len', by reusing 'len', avoid incrementing 'i' when not needed, in the while loop, etc... E.g. for ltrim: char* ltrim(char* string) { char* ret = string; // When either the string is empty, or contains no leading space, // return the same string. unsigned char len = (unsigned char)*string; if (!len || *++string != ' ') { return ret; } // At this point, we already know that first character in string is a space... unsigned char spaces = 1; // Search first non-space character while (spaces < len && *++string == ' ') { ++spaces; } len -= spaces; // Remaining string length = len - spaces // Allocate our return string ret = GetTempMemory(len + 1); // Copy the rest of the string char* s = ret; *s++ = (char)len; // Store BASIC (trimmed) string length while (len--) { *s++ = *string++; } return ret; } |
||||
CaptainBoing Guru Joined: 07/09/2016 Location: United KingdomPosts: 1985 |
here includes all of the old MMbasic library file and a a growing number of articles from various contributors. |
||||
paceman Guru Joined: 07/10/2011 Location: AustraliaPosts: 1328 |
Thanks CaptainB - I have previously had a browse of the code from Hugh's Maximite library that you'd already added but I'm just setting up an account now. How do you intend new code or snippets to be added to the Wiki? Is this going to be up to each account holder or will you be putting useful things like this Cfunction of Michael's into it? Greg BTW: I'm not familiar with how Wiki's operate but I guess I'll learn. Do you have a link to a good primer, preferably less than 20 pages long. |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 8592 |
Memory is reserved in 256-byte chunks so whatever you request it is rounded up to the next amount divisible by 256 |
||||
piclover Senior Member Joined: 14/06/2015 Location: FrancePosts: 134 |
Good to know. In this case, always reserving 256 bytes in ltrim()/rtrim() would be simpler. However, this is an implementation detail that could change in future a Micromite firmware, so the future-proof way to do it is to reserve the amount necessary for the return string... |
||||
piclover Senior Member Joined: 14/06/2015 Location: FrancePosts: 134 |
Please ignore this message; I meant to "Edit" and wrongly pushed "Quote"... and since there's no "Delete post" option on this forum... |
||||
CaptainBoing Guru Joined: 07/09/2016 Location: United KingdomPosts: 1985 |
How do you intend new code or snippets to be added to the Wiki? Is this going to be up to each account holder or will you be putting useful things like this Cfunction of Michael's into it? Greg BTW: I'm not familiar with how Wiki's operate but I guess I'll learn. Do you have a link to a good primer, preferably less than 20 pages long. Hi Greg. Anyone can browse the code etc with no restriction. Having an account allows you to add your own code, modify existing pages (to improve/discuss/correct) etc. I have added code as administrator (and *always* credit) but it is nicer if people add their own because: 1. contributions can be seen by user (by clicking on the user name) which means the credit is given automatically 2. it promotes the idea of community growing the library with no single person as gatekeeper. The problem with libraries is everyone wants them but the push to keep adding fades over time. Wikis are really easy to drive. Posting on TBS has already got you in the mindset of using Markup to make sections stand-out. (One thing that always catches new users is uploading images and attachments - they have to be done before you need them in a page - this allows images to be shared between articles as they are not compartmentalized to a single page.) Here is a nice <20 page primer that gets your pages looking pro - but the idea of a wiki is "do it your way". Have a read of the namespace home-pages. They contain hints for a good experience and any "rules" such as they are. As a very quick crib; choose the namespace (zone) of interest from the drop-down top right, click "Create New Page" on the navi-pane on the left and you are into your new article. Give it a nice title and then write your article in the main body. Markup tags here will make your page look snazzy Tip:Select either of the "code" types to add verbatim code (I like block to make it stand out). You can choose categories at the bottom from the list (or make a new one), add any keywords for searching (not essential) and a brief comment for any changes you make. It will always be non-commercial, free to use and advert free. |
||||
twofingers Guru Joined: 02/06/2014 Location: GermanyPosts: 1133 |
@paceman, thanks for the kind words! @Geoffg Of course! The code is not perfect but simple & short. It's a honor to be in the MM distribution. BTW. Is there a way for a CFunction implemented to inform Basic about a error? Some kind of error handling/trapping? E.G. to set a error number? @piclover Thanks for your contribution! This is meant as a discussion thread therefore your example is highly welcome. Is your code also free for anybody - without any restrictions - like mine? I converted the code into a CFunction and it seems your code is significant faster than mine (for longer strings about 10%) . On the other hand my code takes less memory. This just as information. I learnt from your code and wrote a RTrim() CFunction in your style (hopefully! ). I always appreciate any improvements (any kind, any time)! This is my new C code for RTrim (piclovers style) char* rtrim(char* string) { char* ret = string; // Save string pointer // When either the string is empty, or contains no trailing space, // return the same string. unsigned char len = (unsigned char)*string; if (!len || *(--string+len+1) != ' ') { return ret; } // At this point, we already know that // the last character in string is a space... unsigned char spaces = 1; char* sr=++string+len; // point to the end of the string // Search first non-space character while (spaces <= len && *--sr == ' ') { ++spaces; } len -= spaces; // Remaining string length = len - spaces // Allocate our return string ret = GetTempMemory(256); *ret = (char)len; // Store BASIC (trimmed) string length sr = ret; // Copy pointer to ret string while (len--) {// Copy the string, starting from the first char *++sr = *++string; } return ret; } CFunctions: ' ' piclovers LTrim version ' CFunction ltrim1(STRING) STRING 00000000 27BDFFE0 AFBF001C AFB20018 AFB10014 AFB00010 90920000 1240002C 00801021 80830001 24020020 14620027 24900001 2E420002 1440000F 24020020 26100001 82030000 1462000C 24110001 24030020 26310001 323100FF 12510022 26100001 82020000 5043FFFB 26310001 10000003 02518823 24110001 02518823 323100FF 3C029D00 8C420040 0040F809 24040100 1220000E A0510000 24430001 26040001 2631FFFF 323100FF 00918821 92050000 A0650000 26100001 1611FFFC 24630001 10000003 8FBF001C 00801021 8FBF001C 8FB20018 8FB10014 8FB00010 03E00008 27BD0020 3C029D00 8C420040 0040F809 24040100 02518823 1000FFF4 A0510000 End CFunction ' ' RTrim like piclovers LTrim ' CFunction rtrim1(STRING) STRING 00000000 27BDFFE0 AFBF001C AFB10018 AFB00014 90820000 1040002B 00808021 00821821 80640000 24030020 54830027 02001021 02022021 2483FFFF 8085FFFF 24040020 14A4000C 24110001 24050020 26310001 323100FF 0051202B 14800006 2463FFFF 80640000 5085FFFA 26310001 10000002 00518823 00518823 323100FF 3C029D00 8C420040 0040F809 24040100 1220000E A0510000 26030001 2631FFFF 323100FF 00718821 00401821 24630001 26100001 92040000 1611FFFC A0640000 10000003 8FBF001C 00801021 8FBF001C 8FB10018 8FB00014 03E00008 27BD0020 End CFunction I also wrote a empty CFunction for speed comparison purposes char* empty(char* string) { char* ret = string; return ret; } ' ' empty function, only returns the input string - for speed test ' CFunction empty(STRING) STRING 00000000 03E00008 00801021 End CFunction The result is amazing: the empty function takes (0.186ms on a MX170/28 V5.04.05) about as long as the TRIM() functions. If I put the CFunction at the end of the Basic code then I get 0.203ms and if I use a huge function name (eg "empty_longname()") then takes it 0.22ms. Compared to 0.199ms for a LTrim(" Hello World ") is this a lot. Something to play with: 2017-07-17_132814_CFunction_Trim_tests.zip @CaptainBoing It's of course a great project but I could not find out how to browse. Is there a table of content, kind of? @Peter Thanks for your hint about the GetTempMemory(256)! My fault! Michael |
||||
Geoffg Guru Joined: 06/06/2011 Location: AustraliaPosts: 3165 |
Yes, call the error() function with a string. However defining a string in a CFunction is difficult. Probably the best way to see how it is done is to refer to Peter's tutorials. Geoff Geoff Graham - http://geoffg.net |
||||
CaptainBoing Guru Joined: 07/09/2016 Location: United KingdomPosts: 1985 |
@CaptainBoing Is there a table of content, kind of? Michael Navigation on the left, "All Pages" or "Categories" - make sure you are in the namespace you want (drop-down top right) |
||||
twofingers Guru Joined: 02/06/2014 Location: GermanyPosts: 1133 |
@Geoff Yes, that's what I saw. I think I can do something like this: char *error_str = GetTempMemory(256); error_str[0]=15; error_str[1]='m'; error_str[2]='i'; error_str[3]='s'; error_str[4]='s'; error_str[5]='i'; error_str[6]='n'; error_str[7]='g'; error_str[8]=' '; error_str[9]='s'; error_str[10]='t'; error_str[11]='r'; error_str[12]='i'; error_str[13]='n'; error_str[14]='g'; error_str[15]='!'; // missing arg[0] + arg[1]? if (string != NULL){ len = (unsigned char) string[0]; } else { error(error_str); } Basic output: Error: missing string! The string argument for the error() function is a Basic string (Byte[0] = strlen)? It seems to work as it should, I can even use the "ON ERROR SKIP". Is there a list of MMBasic error messages? I thought I could somehow call some of them from the CFunction. Here is again a list of Peters tutorials for CFunctions: CFunctions - learning by example (1) CFunctions - learning by example (2) CFunctions - learning by example (3) CFunctions - learning by example (4) I scanned all of this and found nothing about the error(). Anyway many thanks! @CaptainBoing, it works, thanks! Seems I'm sometimes blind or too old! Michael |
||||
matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 8592 |
You can call error in the same way as I call MMPrintString in tutorial 4. Make sure all the compiler and linker switches are set exactly as in the tutorial. Here is the error code I use in the MM2 SDcard routines. In the main code I can then just call errstring(error_number); __attribute__((noinline)) void getFPC(void *a, void *b, volatile unsigned int *c) { *c = (unsigned int) (__builtin_return_address (0) - (b -a)) ; } void errstring(int a){ volatile unsigned int libAddr ; getFPC(NULL,&&getFPCLab,&libAddr) ; // warning can be ignored, stupid editor getFPCLab: { } unsigned char *s; static const char err0[]="Unknown error\r\n"; static const char err1[]="Disk Error\r\n"; static const char err2[]="Not Ready\r\n"; static const char err3[]="No File\r\n"; static const char err4[]="Not Opened\r\n"; static const char err5[]="Not Enabled\r\n"; static const char err6[]="No File System\r\n"; static const char err7[]="Not mounted\r\n"; s=(unsigned char *)((void *)err0 + libAddr ); if(a){ if(a==1)s=(unsigned char *)((void *)err1 + libAddr ); if(a==2)s=(unsigned char *)((void *)err2 + libAddr ); if(a==3)s=(unsigned char *)((void *)err3 + libAddr ); if(a==4)s=(unsigned char *)((void *)err4 + libAddr ); if(a==5)s=(unsigned char *)((void *)err5 + libAddr ); if(a==6)s=(unsigned char *)((void *)err6 + libAddr ); if(a==7)s=(unsigned char *)((void *)err7 + libAddr ); error(s); } } |
||||
twofingers Guru Joined: 02/06/2014 Location: GermanyPosts: 1133 |
Here is the error code I use in the MM2 SDcard routines. In the main code I can then just call errstring(error_number); Thanks, Peter! I think that should help. I will try it. Michael EDIT: Found the link to the MM2 SDcard routines EDIT2: Nathan's post helps to understand Peters C code above (Posted: 17 July 2017 at 6:35pm). |
||||
Page 1 of 2 |
Print this page |