Author Topic: String lib for Pelles C (and VC++ 2010 Express)  (Read 11312 times)

jj2007

  • Member
  • *****
  • Posts: 7757
  • Assembler is fun ;-)
    • MasmBasic
String lib for Pelles C (and VC++ 2010 Express)
« on: May 28, 2013, 01:21:25 AM »
When searching Pelles Forum for string libraries, only one post popped up (STR lib for C strings), and it is obviously 'unfinished business'. So I put together a few essential string functions from a (cough, cough) well-known other library. Below an example, attached the library itself.
It's tested with Pelles C and VC++ 2010 Express (open *.sln for the latter). Feedback on problems with these two is welcome (I don't want to add any other compiler).

#include <stdio.h>
#include "Mb4C.h"                  // requires Masm32 & MasmBasic on the same drive as VC++ or Pelles C
#pragma comment(lib, "Mb4C.lib")        // VC++ 2010 Express may want ../File.., depending on your folders
#define ShowLines        30        // how many lines do you want to see?
#define wait4keypress        0        // your IDE may or may not close the console window...

int main(int argc, char *argv[]) {
        int i, NumStrings;
        printf("%s\n", MbStringString(12, "#"));
        printf("%s", MbLeft("Left_____________", 4));
        printf("%s", MbMid("_________Mid___", 10, 3));
        printf("%s", MbRight("____Right\n", 6));        // C thinks \n is one char
        printf("%s\n", MbStringString(0, 0));        // 0, 0 = repeat previous StringString
        // ############        the output...
        // LeftMidRight
        // ############

        // print e.g. 31.12.2013, 12:34:56:
        printf("Date+Time=[%s, %s]\n\n", MbLeft(MbDateString(), 6), MbLeft(MbTimeString(), 5));

        char* MyFile="Mb4C.h";        // take the header file if commandline is empty
        if (argc>1) {
                MyFile=argv[1];        // use the commandline
                printf("The length of the filename passed is %i bytes\n", MbLen(MyFile));
                }
        printf("The extension of %s is [%s]\n", MyFile, MbMid(MyFile, MbInstr(MbLen(MyFile)-5, MyFile, ".", 0), 99));
        printf("The size of the file passed is %i bytes, ", MbFileSize(MyFile));
        printf("while 'NoSuchFile.txt' is %i bytes long\n\n", MbFileSize("NoSuchFile.txt"));

        MbTimer();        // start timing a few lines; QPC resolution is microseconds
        NumStrings=MbRecall(MyFile);          // load a text file into a string array
        MbSetElement(NumStrings+0, MbStringString(25, "+-"));
        MbSetElement(NumStrings+1, "+ you can append as many strings as you like, but");
        MbSetElement(NumStrings+2, "+ make sure you don't assign beyond (lastindex+1)");
        MbSetElement(NumStrings+3, MbStringString(0, 0));
        NumStrings+=4;

        printf("Loading %i strings into an array took %i microseconds\n\n", NumStrings, MbTimer());

        // delete an array element:
        MbDeleteElement(7);
        // insert an array element:
        MbInsertElement(5, "\t## New element 5 ##");

        // modify an array element for later display:
        MbSetElement(1, MbCatString("*** String #1: [", MbGetElement(1), "] ***", 0));

        // save the modified array
        MbStore("MyTest.tmp");

        // assign a simple text to MyString and print it:
        char* MyString=0;
        // first arg of MbSetString() must be the destination (clumsy but important):
        MyString=MbSetString(MyString, "-- Let's make a test: --");
        printf("MyString A=\t%s\n", MyString);

        // modify MyString using MbCatString and an array element:
        MyString=MbSetString(MyString, MbCatString("xx_", MbGetElement(1), "_xx", 0));
        printf("MyString B=\t%s\n", MyString);

        MyString=MbSetString(MyString, "-- end of test --");
        printf("MyString C=\t%s\n\n", MyString);

        // print first 50 chars of array elements in a loop (#1 and #5 are modified):
        for (i=0; i<= (NumStrings<ShowLines ? NumStrings-1 : ShowLines); i++) {
                printf("%i\t%s\n", i, MbLeft(MbGetElement(i), 50));        // print the first n strings
                }

        #if wait4keypress
                printf("\n** Hit Return to close the console window **");
                while(getchar()!='\n') {};
        #endif
        return 0;
}


EDIT: Attachment removed, see reply #11 for the latest version.
« Last Edit: May 31, 2013, 07:11:13 AM by jj2007 »

Vortex

  • Moderator
  • Member
  • *****
  • Posts: 1733
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #1 on: May 28, 2013, 03:30:26 AM »
Hi Jochen,

Thanks for your contribution.

Gunther

  • Member
  • *****
  • Posts: 3518
  • Forgive your enemies, but never forget their names
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #2 on: May 28, 2013, 05:31:07 AM »
Hi Jochen,

good job, thank you. Will it work with other compilers, too?

Gunther
Get your facts first, and then you can distort them.

jj2007

  • Member
  • *****
  • Posts: 7757
  • Assembler is fun ;-)
    • MasmBasic
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #3 on: May 28, 2013, 05:36:41 AM »
Will it work with other compilers, too?

Test it... you are using gcc, right?

Gunther

  • Member
  • *****
  • Posts: 3518
  • Forgive your enemies, but never forget their names
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #4 on: May 28, 2013, 06:57:48 AM »
Will it work with other compilers, too?

Test it... you are using gcc, right?

Right. I give it a try next wednesday and leave a message for you.

Gunther
Get your facts first, and then you can distort them.

Vortex

  • Moderator
  • Member
  • *****
  • Posts: 1733
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #5 on: May 29, 2013, 03:14:29 AM »
Here is the header file converted to Masm :

Code: [Select]
MbCatString PROTO C :VARARG
MbDateString PROTO
MbDeleteElement PROTO :DWORD
MbDtoa PROTO :DWORD,:DWORD,:DWORD
MbFileSize PROTO :DWORD
MbGetElement PROTO :DWORD
MbInsertElement PROTO :DWORD,:DWORD
MbInstr PROTO :DWORD,:DWORD,:DWORD,:DWORD
MbItoa PROTO :DWORD
MbLeft PROTO :DWORD,:DWORD
MbLen PROTO :DWORD
MbMid PROTO :DWORD,:DWORD,:DWORD
MbRecall PROTO :DWORD
MbRight PROTO :DWORD,:DWORD
MbSetElement PROTO :DWORD,:DWORD
MbSetString PROTO :DWORD,:DWORD
MbStore PROTO :DWORD
MbStringString PROTO :DWORD,:DWORD
MbTimer PROTO
MbTimeString PROTO

anta40

  • Member
  • ***
  • Posts: 293
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #6 on: May 29, 2013, 12:15:52 PM »
Another interesting lib is Better String Library

http://bstring.sourceforge.net/bstrFAQ.shtml
http://bstring.sourceforge.net/features.html

It seems to be a solid, well-designed lib. I never use it extensively, though.
But at least it's very easy to build  :biggrin:

jj2007

  • Member
  • *****
  • Posts: 7757
  • Assembler is fun ;-)
    • MasmBasic
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #7 on: May 29, 2013, 03:08:00 PM »
Can you translate my example above to the BSL, so that we have a comparison? Or at least this part, just to get an idea:

   printf("%s\n", MbStringString(12, "#"));
   printf("%s", MbLeft("Left_____________", 4));
   printf("%s", MbMid("_________Mid___", 10, 3));
   printf("%s", MbRight("____Right\n", 6));   // C thinks \n is one char
   printf("%s\n\n", MbStringString(0, 0));   // 0, 0 = repeat previous StringString
   // ############   the output...
   // LeftMidRight
   // ############


I am also curious how it handles string concatenation, like this simple example:

  char* MyStr="middle";        // simple string concatenation
  MyStr=MbSetString(MyStr, MbCatString(" left ", MyStr, " right "));        // ' left middle right '
  for (i=0; i<=25; i++) {
        MyStr=MbSetString(MyStr, MbCatString("*", MyStr, "*"));        // add stars to the left and right
        }
  // output: ************************** left middle right **************************
  printf("%s\n", MyStr);


Thanks, JJ

P.S.
> But at least it's very easy to build
I've downloaded bstrlib-05122010.zip but it seems not to contain any examples or building instructions...
« Last Edit: May 29, 2013, 05:26:39 PM by jj2007 »

anta40

  • Member
  • ***
  • Posts: 293
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #8 on: May 30, 2013, 02:26:16 AM »
Hi jj,

Can you translate my example above to the BSL, so that we have a comparison? Or at least this part, just to get an idea:

   printf("%s\n", MbStringString(12, "#"));
   printf("%s", MbLeft("Left_____________", 4));
   printf("%s", MbMid("_________Mid___", 10, 3));
   printf("%s", MbRight("____Right\n", 6));   // C thinks \n is one char
   printf("%s\n\n", MbStringString(0, 0));   // 0, 0 = repeat previous StringString
   // ############   the output...
   // LeftMidRight
   // ############


I am also curious how it handles string concatenation, like this simple example:

  char* MyStr="middle";        // simple string concatenation
  MyStr=MbSetString(MyStr, MbCatString(" left ", MyStr, " right "));        // ' left middle right '
  for (i=0; i<=25; i++) {
        MyStr=MbSetString(MyStr, MbCatString("*", MyStr, "*"));        // add stars to the left and right
        }
  // output: ************************** left middle right **************************
  printf("%s\n", MyStr);


Please run the executable from the zipped project.
It doesn't handle MbStringString(0, 0) properly. It does handle string concatenation, though (look for bconcat).
Hopefully looking the C code won't make your head dizzy  :biggrin:

I've downloaded bstrlib-05122010.zip but it seems not to contain any examples or building instructions...

It does contain the documentation and 1 test program.
Take a look at bstrlib.txt
Quote
C projects need only include bstrlib.h and compile/link bstrlib.c to use the
bstring library.

If you want more examples, take a look at this:
http://prdownloads.sourceforge.net/bstring/examples-09282006.zip?download

Hope this helps  ;)

jj2007

  • Member
  • *****
  • Posts: 7757
  • Assembler is fun ;-)
    • MasmBasic
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #9 on: May 30, 2013, 05:54:20 AM »
It does contain the documentation and 1 test program.
Take a look at bstrlib.txt

C projects need only include bstrlib.h and compile/link bstrlib.c to use the
bstring library.

Thanks a lot, anta :icon14:

Sorry for my ignorance: Where (which line) in bstrlib.txt is the building of the library documented? Or does one have to add bstrlib.c to the compilers commandline?? Would that work with Pelles C? Most libraries have one or more *.lib files plus headers... ::)

EDIT: Trial & error succeeded:
- compile bstrlib.c (the linker will complain about a missing _main, but no problem because we are only interested in bstrlib.obj)

Assuming you have folders...
\Masm32\PellesC\Bin
\Masm32\PellesC\Include
\Masm32\PellesC\BSL
\Masm32\PellesC\Projects\BstrExamples

... then in your source \Masm32\PellesC\Projects\BstrExamples\Hello World app:

- #include "../../BSL/bstrlib.h
- add ../../BSL/bstrlib.obj to the linker's commandline
... and voilĂ , it works :t

So if I understand it correctly, you need to build your own functions, e.g. bsl_left():
// bstring b1 = bsl_left("Left_____________", 4);
bstring bsl_left(char *str, int len){
   char* result = (char*) malloc(sizeof(char) * (len+1));
   memset(result, '\0', len + 1);
   strncpy(result, str, len);
   return bfromcstr(result);
}

Where is the corresponding mfree, or is it not needed?

Still playing  :P
   bconcat(b1, b2);   // OK
   printf("New b1=[%s]", (char *) b1->data);
   // bconcat(b1, b2, b3);   // too many args
   // b1 = bsl_left(b1, 2);   // type error in argument 1 to 'bsl_left'; expected 'char *' but found 'bstring'
« Last Edit: May 30, 2013, 07:04:55 AM by jj2007 »

anta40

  • Member
  • ***
  • Posts: 293
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #10 on: May 30, 2013, 12:53:00 PM »
Sorry for my ignorance: Where (which line) in bstrlib.txt is the building of the library documented? Or does one have to add bstrlib.c to the compilers commandline?? Would that work with Pelles C? Most libraries have one or more *.lib files plus headers... ::)

Ah I see. Well it's not stated in the document.
The document assumes you are working directly in C.
If you want to build bstrlib as a lib file, which can be conveniently linked with asm code, then I guess you have to dig your compiler's manual

So if I understand it correctly, you need to build your own functions, e.g. bsl_left():
// bstring b1 = bsl_left("Left_____________", 4);
bstring bsl_left(char *str, int len){
   char* result = (char*) malloc(sizeof(char) * (len+1));
   memset(result, '\0', len + 1);
   strncpy(result, str, len);
   return bfromcstr(result);
}

Where is the corresponding mfree, or is it not needed?

In this case: yes. The documentations says that "...functionality that can be implemented trivially in other functionality is omitted. 
So there is no left$() or right$() or reverse() or anything like that as part of the core functionality."

And yes, you're right. All memory allocated must be de-allocated explicitely. I think we can achive this by:
Code: [Select]
bdestroy(b0);
bdestroy(b1);
bdestroy(b2);
bdestroy(b3);

right after printing those objects

Still playing  :P
   // bconcat(b1, b2, b3);   // too many args
   // b1 = bsl_left(b1, 2);   // type error in argument 1 to 'bsl_left'; expected 'char *' but found 'bstring'


bconcat is only able to handle 2 arguments. it is possible though to write an 'extended bconcat',
so it is able to handle arbitratry arguments. we can use va_list for that purpose

my bsl_left doesn't work that way because it expects its first arg to be a char *, (like most bstrlib functions do). it can be done by adding a few lines of code  :P

jj2007

  • Member
  • *****
  • Posts: 7757
  • Assembler is fun ;-)
    • MasmBasic
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #11 on: May 31, 2013, 07:10:11 AM »
OK, I managed to use simultaneously BSL and my own (small) subset of MasmBasic. Attached an archive that contains all the necessary except the BSL lib itself, which should be downloaded from here ("DOWNLOAD" to the left, near the top) and extracted to \Masm32\PellesC\BSL

\Masm32\PellesC\Projects\StringLibs\bsltest.c contains anta40's code plus a first attempt to do some benchmarking, see ## ARRAY TEST START ##.

The benchmark is simple: Load Windows.inc into a buffer and transform the buffer into a string array.

I suspect this is possible with BSL's bsplit / bsplits / bsplitstr functions but I have not been able to get it working. Grateful for a helping hand :icon14:

There are a handful of new additions to the MB lib, such as MbFileRead and MbStringToArray. Maybe it's just that I don't have enough experience with C, but I get more and more respect of the Masm macro engine, despite its bugs - it is much more powerful than the C preprocessor, it seems...

By the way, what happened to BSL author Paul Hsieh? He has an interesting page on optimisation here, but it seems ten years old.

anta40

  • Member
  • ***
  • Posts: 293
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #12 on: May 31, 2013, 12:29:29 PM »
it is much more powerful than the C preprocessor, it seems...

The task of C preprocessor, if I'm not mistaken, is not more then doing text-substitution, e.g:

Code: [Select]
#define multiply(A,B) (A*B)

So
Code: [Select]
multiply(1+2, 1+3) will be expanded to:

Code: [Select]
1 + 2*1 + 3
As simple as that. Sure you can do lots of wacky stuffs with C pre-processor, but...  :icon_mrgreen:

On the other hand, how is MASM macro different? Personally I've never learn it. Well I think I'm going to learn it.  :bgrin:

jj2007

  • Member
  • *****
  • Posts: 7757
  • Assembler is fun ;-)
    • MasmBasic
Re: String lib for Pelles C (and VC++ 2010 Express)
« Reply #13 on: May 31, 2013, 08:15:10 PM »
On the other hand, how is MASM macro different?

Try doing something like this in C:

include \masm32\MasmBasic\MasmBasic.inc        ; download
.data?
MyByte        db ?
MyWord        dw ?
MyDw        dd ?
MyQw        dq ?
MyR4        REAL4 ?
MyR10        REAL10 ?

        Init
        mov ecx, Chr$("123.456")        ; a string to be converted to a number
        MovVal MyByte, ecx
        MovVal MyWord, ecx
        mov ecx, Chr$("123456.789")
        MovVal MyDw, ecx
        MovVal MyQw, ecx
        MovVal MyR4, ecx
        MovVal MyR10, ecx
        MovVal xmm0, ecx         ; xmm0 as integer (default)
        MovVal f:xmm1, ecx        ; xmm1 as float (f:)
        Dim My$()                     ; create a string array (yes, that could be more complicated ;))
        For_ ebx=0 To 5
                Let My$(ebx)=Str$("String #%i=", ebx)+Str$(ebx*MyR4)        ; assign some values, e.g. 3*123456.78
                PrintLine "[", My$(ebx), "]"
        Next

        push Val(ecx)        ; the simple Val returns eax
        fild dword ptr [esp]        ; get the FPU involved
        pop eax
        ; debug macro - does not trash any regs or flags, can display almost everything...
        deb 4, "Results:", eax, ST(0), MyByte, MyWord, MyDw, MyQw, MyR4, MyR10, xmm0, f:xmm1
        Inkey CrLf$, "--- cute, isn't it?"
        Exit
end start

Output:

[String #0=0.0]
[String #1=123456.8]
[String #2=246913.6]
[String #3=370370.4]
[String #4=493827.2]
[String #5=617283.9]

Results:
eax             123457
ST(0)           123457.000000000000
MyByte          123
MyWord          123
MyDw            123457
MyQw            123457
MyR4            123456.8
MyR10           123456.789000000000
xmm0            123457
f:xmm1          123456.7890000000


P.S.: I liked your wacky macros page - #define sizeof(x) rand() looks great for bug chasing ;-)

P.P.S.:
Load Windows.inc into a buffer and transform the buffer into a string array.

I suspect this is possible with BSL's bsplit / bsplits / bsplitstr functions but I have not been able to get it working.

Found an example by Paul Hsieh himself but it fails miserably with loads of error messages :(

However, I got this working; it loads the correct #strings, and the timings are plausible, but I can't figure out how to use the 'tokens' for string operations including printing:

/*        called as follows - WinStr is a pointer to a buffer containing Windows.inc:
        bstring MyArr1 = bfromcstr(WinStr);
        rv=ArrayJJ(MyArr1);
*/

int ArrayJJ(const_bstring src) {          // bstrlib.c, line 2655
        struct bstrList* tokens;
        int ct;
        tokens = bsplit(src, '\n');        // translates source string to an array
//         push 0Ah        // linefeed
//         push ebx        // src
//         call bsplit

        __asm mov ct, ecx;        // returns number of strings (undocumented?)
        #if 0
                // __asm int 3;
                // how can we use the array???
                // compiles but prints garbage:
                printf("T0=%s\n", tokens->entry[0]);
                printf("T1=%s\n", tokens->entry[1]);
                printf("T2=%s\n", tokens->entry[2]);
        #endif
        return ct;
        }
« Last Edit: June 01, 2013, 12:33:15 AM by jj2007 »

jj2007

  • Member
  • *****
  • Posts: 7757
  • Assembler is fun ;-)
    • MasmBasic
Timings for string to array, BSL vs MB4C
« Reply #14 on: June 01, 2013, 09:30:11 AM »
I got it working, thanks to a little help from Pelles C Forum :t

Timings are for 200 * translating Windows.inc to an array:
2654 milliseconds for 26902 strings with bsplit
638  milliseconds for 26902 strings with StringToArray


So Assembler wins, as usual :biggrin:
It also seems that the BSL needs about twice as much memory for the buffer-to-array conversion.

Here are the respective subprocs (see also attachment); src points to a buffer containing Windows.inc:

int TestLoadMB(char* src, int show) {
        int ct = MbStringToArray(src);        // translates source string to an array
        if (show>=testloops) {        // show first and last array element in last loop
                printf("#0 MB=\t%s\n", MbGetElement(0));
                printf("#n MB=\t%s\n\n", MbGetElement(ct-1));
                }
        return ct;
        }

int TestLoadBSL(const_bstring src, int show) {
        int ct;
        struct bstrList* tokens;          // bstrlib.c, line 2655
        tokens = bsplit(src, '\n');        // translates source string to an array
        __asm mov ct, ecx;        // returns number of strings (undocumented?)
        if (show>=testloops) {        // show first and last array element in last loop
                printf("#0 Bsl=\t%s\n", tokens->entry[0]->data);
                printf("#n Bsl=\t%s\n\n", bdata(tokens->entry[ct-1]));
                }
        bstrListDestroy(tokens);        // if not freed, mem usage will rise to e.g. 300 MB for 100 testloops
        return ct;
        }