New commands PureBasic: StringField, Space, byte-calc

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
thamarok
Enthusiast
Enthusiast
Posts: 282
Joined: Wed Sep 06, 2006 1:37 pm

Post by thamarok »

AND51 wrote:
What the hell?
First you copy my code and say that I didn't use protected on the variable i?
And it was not about optimization, I made the smallest code for this!
Sorry. I can understand your point of view. Unfortunately, I had the wrong code in my clipboard. I corrected to make you see what I mean.

In my opinion, not the smallest, but the fastest code is the code that can be declared as "optimized". I discovered this, when there was the byte-calc contest in the german forum. It's no problem for me to turn my byte-calc into a one-liner (without : !!). But then the code get slower! Although I dont' work with variables any more. So you can see: Smallest code <> fastest performance
I would suggest: let's kepp the contest alive! :)
Ok, no problem I am not mad at you anymore :)
Good to see you had your explanation, but the next time, please check what you have in the clipboard :wink:

And about one-liners, I always code like that, I just like small sources even how dirty and complex they are, but I have written a little program which turns my one-liners to full PB code, so that you people would understand them easily (especially newcomers).

Let's have peace and concentrate to the upcoming stuff.
Guten abend noch AND51 und nco2k!
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Post by wilbert »

There's a problem with your swapfields procedure.
Compare the two results. This also proves pointers can be faster (ok, with a little help from asm)

Code: Select all

Procedure.s StringField_SwapFields_Orig(String$, FieldA, FieldB, Separator$=" ") ; by AND51
   If FieldA <> FieldB
      String$+Separator$
      If FieldA > FieldB
         Swap FieldA, FieldB
      EndIf
      Protected FieldA$=StringField(String$, FieldA+1, Separator$), FieldB$=StringField(String$, FieldB+1, Separator$)
      FieldA=FindString(String$, FieldA$, 1)
      FieldB=FindString(String$, FieldB$, 1)
      ProcedureReturn Left(Left(String$, FieldA-1)+FieldB$+Mid(String$, FieldA+Len(FieldA$), FieldB-FIeldA-Len(FieldA$))+FieldA$+Right(String$, Len(String$)-(FieldB+Len(FieldB$))+1), Len(String$)-Len(Separator$))
   Else
      ProcedureReturn String$
   EndIf
EndProcedure

Procedure.s StringField_SwapFields(String.s, FieldA.l, FieldB.l, Separator.s = " ")
 Protected *Orig.l, *Copy.l, PosA.l, PosB.l, LenA.l, LenB.l
 Protected esi.l,ebx.l
 ; esp + 48 contains ptr to the original string
 !mov eax,dword [esp+48]
 !mov dword [p.p_Orig],eax
 ; passed String contains a copy
 !mov eax,dword [p.v_String]
 !mov dword [p.p_Copy],eax
 ; swap fields if required and check if fields > 0 and not equal
 !mov eax,[p.v_FieldA]
 !mov edx,[p.v_FieldB]
 !cmp edx,eax
 !je sf_err
 !jg sf_cont
 !xchg eax,edx
 !mov [p.v_FieldA],eax
 !mov [p.v_FieldB],edx
 !sf_cont:
 !cmp eax,1
 !jl sf_err
 ; store esi and ebx registers
 !mov [p.v_esi],esi
 !mov [p.v_ebx],ebx
 ; scan for both fields
 !mov edx,[p.v_Separator]
 !mov ah,[edx]
 !mov esi,[p.v_String]
 !xor ecx,ecx
 !cld
 !sf_pre_loop:
 !mov edx,esi
 !sf_scan_loop:
 !lodsb
 !and al,al
 !jz sf_end_fnd
 !cmp al,ah
 !jnz sf_scan_loop
 !inc ecx
 !cmp ecx,[p.v_FieldB]
 !jz sf_fldB_fnd
 !cmp ecx,[p.v_FieldA] 
 !jnz sf_pre_loop
 !mov ebx,edx
 !sub ebx,[p.v_String]
 !mov [p.v_PosA],ebx
 !mov ebx,esi
 !sub ebx,edx
 !dec ebx
 !mov [p.v_LenA],ebx
 !jmp sf_pre_loop
 !sf_fldB_fnd:
 !mov ebx,edx
 !sub ebx,[p.v_String]
 !mov [p.v_PosB],ebx
 !mov ebx,esi
 !sub ebx,edx
 !dec ebx
 !mov [p.v_LenB],ebx
 !sf_end_fnd:
 !inc ecx
 !cmp ecx,[p.v_FieldB]
 !jz sf_fldB_fnd
 ; retrieve esi and ebx registers
 !mov esi,[p.v_esi]
 !mov ebx,[p.v_ebx]
 ; check if both fields exist
 !cmp byte [p.v_PosB],0
 !je sf_err
 MoveMemory(*Orig+PosB,*Copy+PosA,LenB)
 MoveMemory(*Orig+PosA,*Copy+PosB+LenB-LenA,LenA)
 MoveMemory(*Orig+PosA+LenA,*Copy+PosA+LenB,PosB-PosA-LenA)
 !sf_err:
 ProcedureReturn String
EndProcedure 

t = GetTickCount_()
s.s = "hai there, this is a test to see if swapping some fields will work for this string."
For i=1 To 100001
 s = StringField_SwapFields_Orig(s,0,3)
Next
Debug s
t = GetTickCount_()-t
Debug t

t = GetTickCount_()
s.s = "hai there, this is a test to see if swapping some fields will work for this string."
For i=1 To 100001
 s = StringField_SwapFields(s,1,4)
Next
Debug s
t = GetTickCount_()-t
Debug t
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

Hello! Thanks for your code!
I'm not able to code with / in ASM... I even don't uinderstand 'mov' :oops:

I corrected my code; there was a bug concerning the first field. I made the last field a field by appending Seperator$. But i also must add Seperator$ before the beginning to make the first field a field.

You know what I mean?

This should work now. Code becomes slower, because not only appending one Seperator$, but embedding the whole String into Seperators$. Second fact, why code got slower is that I used Mid() instead of Left() (neccessary).

Code: Select all

Procedure.s StringField_SwapFields(String$, FieldA, FieldB, Separator$=" ") ; by AND51
	If FieldA <> FieldB
		String$=Separator$+String$+Separator$
		If FieldA > FieldB
			Swap FieldA, FieldB
		EndIf
		Protected FieldA$=StringField(String$, FieldA+1, Separator$), FieldB$=StringField(String$, FieldB+1, Separator$)
		FieldA=FindString(String$, FieldA$, 1)
		FieldB=FindString(String$, FieldB$, 1)
		ProcedureReturn Mid(Left(String$, FieldA-1)+FieldB$+Mid(String$, FieldA+Len(FieldA$), FieldB-FIeldA-Len(FieldA$))+FieldA$+Right(String$, Len(String$)-(FieldB+Len(FieldB$))+1), Len(Separator$)+1, Len(String$)-Len(Separator$)*2)
	Else
		ProcedureReturn String$
	EndIf
EndProcedure
PB 4.30

Code: Select all

onErrorGoto(?Fred)
thamarok
Enthusiast
Enthusiast
Posts: 282
Joined: Wed Sep 06, 2006 1:37 pm

Post by thamarok »

Thank God I came from ASM before coming to PureBasic :wink: .
mov is an instruction which moves data from X to Y.
An example:
The register ax needs to be filled with the value 4, to do this you would make this code:

Code: Select all

MOV ax, 4
Please note that every register has it's own meaning, and setting a number to a register works always as a flag to tell more detail for the processor about the instruction.

Look to the PureBasic FAQ, I think there is a link to a help file with ASM instructions and other stuff.
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Post by AND51 »

Thank you!

Before your explanation, I only knew that 'mov' is needed in evry program. Seems so.

I'll care about ASM, if I'm in the right mood. Although I think that I'm an advanced coder, :wink:
ASM is still to difficult for me.

Nevertheless, I'll pay attention to your advice, thx!

By the way: You filled 'ax' with '4'. Is 'ax' now a .l, .b or .q ? Moreover, can I continue using 'ax' as 'normal PureBasic variable'? I assume, filling variables that way you did is faster than 'ax=4' for example.
PB 4.30

Code: Select all

onErrorGoto(?Fred)
thamarok
Enthusiast
Enthusiast
Posts: 282
Joined: Wed Sep 06, 2006 1:37 pm

Post by thamarok »

AND51 wrote:Thank you!

Before your explanation, I only knew that 'mov' is needed in evry program. Seems so.

I'll care about ASM, if I'm in the right mood. Although I think that I'm an advanced coder, :wink:
ASM is still to difficult for me.

Nevertheless, I'll pay attention to your advice, thx!

By the way: You filled 'ax' with '4'. Is 'ax' now a .l, .b or .q ? Moreover, can I continue using 'ax' as 'normal PureBasic variable'? I assume, filling variables that way you did is faster than 'ax=4' for example.
In the example above, ax is a register and it can be filled with a byte, word etc.. But you can't use a register in PureBasic as a value, but you can do this:

Code: Select all

Debug a
!MOV [v_a], 4 ; To use a PB variable in ASM, you have to put the v_ prefix.
Debug a
I am not sure if this is faster, you have to make a speed test.
I hope this helps

EDIT: I forgot to tell that you need to define (or mention) every variable you are going to use in inlined ASM or you will get a "undefined symbol" error.
Last edited by thamarok on Tue Oct 17, 2006 3:13 pm, edited 3 times in total.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Post by wilbert »

AND51 wrote:This should work now.
Unfortunately it still returns a strange result when I try it with the string I used.
I also included a similar partly asm solution to remove a field.

PureBasic4 code

Code: Select all

Procedure.s StringField_SwapFields_Orig(String$, FieldA, FieldB, Separator$=" ") ; by AND51
   If FieldA <> FieldB
      String$=Separator$+String$+Separator$
      If FieldA > FieldB
         Swap FieldA, FieldB
      EndIf
      Protected FieldA$=StringField(String$, FieldA+1, Separator$), FieldB$=StringField(String$, FieldB+1, Separator$)
      FieldA=FindString(String$, FieldA$, 1)
      FieldB=FindString(String$, FieldB$, 1)
      ProcedureReturn Mid(Left(String$, FieldA-1)+FieldB$+Mid(String$, FieldA+Len(FieldA$), FieldB-FIeldA-Len(FieldA$))+FieldA$+Right(String$, Len(String$)-(FieldB+Len(FieldB$))+1), Len(Separator$)+1, Len(String$)-Len(Separator$)*2)
   Else
      ProcedureReturn String$
   EndIf
EndProcedure

Procedure.s StringField_RemoveField_Orig(String$, Index, Separator$=" ") ; by AND51
   Protected n.l=CountString(String$, Separator$), position.q
   If Index > n
      Index=n
   EndIf
   For n=1 To Index
      position=FindString(String$, Separator$, position+1)
   Next
   ProcedureReturn Left(String$, position)+Right(String$, Len(String$)-(position+Len(StringField(String$, Index+1, Separator$)))-Len(Separator$))
EndProcedure

Procedure.s StringField_SwapFields(String.s, FieldA.l, FieldB.l, Separator.s = " ")
 Protected *Orig.l, *Copy.l, PosA.l, PosB.l, LenA.l, LenB.l
 Protected esi.l,ebx.l
 ; esp + 48 contains ptr to the original string
 !mov eax,dword [esp+48]
 !mov dword [p.p_Orig],eax
 ; passed String contains a copy
 !mov eax,dword [p.v_String]
 !mov dword [p.p_Copy],eax
 ; swap fields if required and check if fields > 0 and not equal
 !mov eax,[p.v_FieldA]
 !mov edx,[p.v_FieldB]
 !cmp edx,eax
 !je sf_err
 !jg sf_cont
 !xchg eax,edx
 !mov [p.v_FieldA],eax
 !mov [p.v_FieldB],edx
 !sf_cont:
 !cmp eax,1
 !jl sf_err
 ; store esi and ebx registers
 !mov [p.v_esi],esi
 !mov [p.v_ebx],ebx
 ; scan for both fields
 !mov edx,[p.v_Separator]
 !mov ah,[edx]
 !mov esi,[p.v_String]
 !xor ecx,ecx
 !cld
 !sf_pre_loop:
 !mov edx,esi
 !sf_scan_loop:
 !lodsb
 !and al,al
 !jz sf_end_fnd
 !cmp al,ah
 !jnz sf_scan_loop
 !inc ecx
 !cmp ecx,[p.v_FieldB]
 !jz sf_fldB_fnd
 !cmp ecx,[p.v_FieldA] 
 !jnz sf_pre_loop
 !mov ebx,edx
 !sub ebx,[p.v_String]
 !mov [p.v_PosA],ebx
 !mov ebx,esi
 !sub ebx,edx
 !dec ebx
 !mov [p.v_LenA],ebx
 !jmp sf_pre_loop
 !sf_fldB_fnd:
 !mov ebx,edx
 !sub ebx,[p.v_String]
 !mov [p.v_PosB],ebx
 !mov ebx,esi
 !sub ebx,edx
 !dec ebx
 !mov [p.v_LenB],ebx
 !sf_end_fnd:
 !inc ecx
 !cmp ecx,[p.v_FieldB]
 !jz sf_fldB_fnd
 ; retrieve esi and ebx registers
 !mov esi,[p.v_esi]
 !mov ebx,[p.v_ebx]
 ; check if both fields exist
 !cmp byte [p.v_PosB],0
 !je sf_err
 MoveMemory(*Orig+PosB,*Copy+PosA,LenB)
 MoveMemory(*Orig+PosA,*Copy+PosB+LenB-LenA,LenA)
 MoveMemory(*Orig+PosA+LenA,*Copy+PosA+LenB,PosB-PosA-LenA)
 !sf_err:
 ProcedureReturn String
EndProcedure 

Procedure.s StringField_RemoveField(String.s, Field.l, Separator.s = " ")
 Protected *Orig.l, *Copy.l, PosF.l, LenF.l, LenS.l
 Protected esi.l,ebx.l
 ; esp + 44 contains ptr to the original string
 !mov eax,dword [esp+44]
 !mov dword [p.p_Orig],eax
 ; passed String contains a copy
 !mov eax,dword [p.v_String]
 !mov dword [p.p_Copy],eax
 ; swap fields if required and check if fields > 0 and not equal
 !mov eax,[p.v_Field]
 !cmp eax,1
 !jl rf_err
 ; store esi and ebx registers
 !mov [p.v_esi],esi
 !mov [p.v_ebx],ebx
 ; scan for field
 !mov edx,[p.v_Separator]
 !mov ah,[edx]
 !mov esi,[p.v_String]
 !xor ecx,ecx
 !cld
 !rf_pre_loop:
 !mov edx,esi
 !rf_scan_loop:
 !lodsb
 !and al,al
 !jz rf_end_fnd
 !cmp al,ah
 !jnz rf_scan_loop
 !inc ecx
 !cmp ecx,[p.v_Field]
 !jnz rf_pre_loop
 !rf_fld_fnd:
 !mov ebx,edx
 !sub ebx,[p.v_String]
 !mov [p.v_PosF],ebx
 !mov ebx,esi
 !sub ebx,edx
 !dec ebx
 !mov [p.v_LenF],ebx
 !cmp dword [p.v_LenS],0
 !jz rf_scan_loop
 !rf_end_fnd:
 !mov ebx,esi
 !sub ebx,[p.v_String]
 !dec ebx
 !mov [p.v_LenS],ebx
 !inc ecx
 !cmp ecx,[p.v_Field]
 !jz rf_fld_fnd
 ; retrieve esi and ebx registers
 !mov esi,[p.v_esi]
 !mov ebx,[p.v_ebx]
 ; check if field length > 0
 !cmp byte [p.v_LenF],0
 !je rf_err
 If LenF = LenS
  PokeB(*Copy,0)
 Else
  MoveMemory(*Orig+PosF+LenF+1,*Copy+PosF,LenS-PosF-LenF)
  PokeB(*Copy+LenS-LenF-1,0)
 EndIf
 !rf_err:
 ProcedureReturn String
EndProcedure 

t = GetTickCount_()
s.s = "hai there, this is a test to see if swapping some fields will work for this string."
For i=1 To 100001
 s = StringField_SwapFields_Orig(s,0,3)
Next
Debug s
t = GetTickCount_()-t
Debug t

t = GetTickCount_()
s.s = "hai there, this is a test to see if swapping some fields will work for this string."
For i=1 To 100001
 s = StringField_SwapFields(s,1,4,".")
Next
Debug s
t = GetTickCount_()-t
Debug t

t = GetTickCount_()
s.s = "hai there, this is a test to see if removing a field will work for this string."
For i=1 To 100001
 s1.s = StringField_RemoveField_Orig(s,13)
Next
Debug s1
t = GetTickCount_()-t
Debug t

t = GetTickCount_()
s.s = "hai there, this is a test to see if removing a field will work for this string."
For i=1 To 100001
 s1.s = StringField_RemoveField(s,14)
Next
Debug s1
t = GetTickCount_()-t
Debug t
naw
Enthusiast
Enthusiast
Posts: 573
Joined: Fri Apr 25, 2003 4:57 pm

Re: Space2

Post by naw »

How about this:

Code: Select all

Procedure.s space2(L,S$)
  ProcedureReturn(ReplaceString(Space(L)," ",S$))
EndProcedure

messageRequester("",space2(10,"PB"))
Ta - N
thamarok
Enthusiast
Enthusiast
Posts: 282
Joined: Wed Sep 06, 2006 1:37 pm

Re: Space2

Post by thamarok »

naw wrote:How about this:

Code: Select all

Procedure.s space2(L,S$)
  ProcedureReturn(ReplaceString(Space(L)," ",S$))
EndProcedure

messageRequester("",space2(10,"PB"))
naw, you are a genious!
That works, is a bit slow, but cool :wink:

What makes it slow is that PB needs to first create the string with space characters, and then replace all of them with the needed string.

A faster way is what I made which does only add the needed amount of the string.
naw
Enthusiast
Enthusiast
Posts: 573
Joined: Fri Apr 25, 2003 4:57 pm

Post by naw »

thamarok thank you :lol: I'm inventive but certainly not a genius but I'll be grinning about the generous compliment all day now...
Ta - N
AND51
Addict
Addict
Posts: 1040
Joined: Sun Oct 15, 2006 8:56 pm
Location: Germany
Contact:

Re: Space2

Post by AND51 »

naw wrote:How about this:

Code: Select all

Procedure.s space2(L,S$)
  ProcedureReturn(ReplaceString(Space(L)," ",S$))
EndProcedure

messageRequester("",space2(10,"PB"))
Of course, I had this idea before creating my Procedure. But your Code does not use th advantage: If 'L' is an even number (e. g. 2, 8, 64, 200, ...) and then length of 'S$' is also even, than you can use the "in place"-method, offered by ReplaceString(). See help for more details.
PB 4.30

Code: Select all

onErrorGoto(?Fred)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Space2

Post by wilbert »

AND51 wrote:than you can use the "in place"-method, offered by ReplaceString().
Sorry, but if you do a speed comparisson you will see that even in that case the solution offered by Naw is faster. The only time your solution is faster is when the fill string consists of spaces only.
Post Reply