Structured pointer problem

Just starting out? Need help? Post your questions and find answers here.
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

The reason why the structure solution does not work is that a String inside
a structure is referenced through a pointer to that string only. (because of its variable length)

The "String" structure looks like this:

Code: Select all

Structure String
  s.s
EndStructure
So when doing *Struct.String, you actually have a pointer (the structure pointer)
to a pointer (the s.s) to a string (the actual data)
Doing "@String$" however returns the pointer to the actual data.
This is a fundamental difference to the other basic types, where the variable data
is actually stored inside the structure, and that is why it will not work with strings.

This one illustrates it:

Code: Select all

String$ = "Hello World"
*Pointer = @String$        ; get pointer to string data
*Struct.String = @*Pointer ; get pointer to pointer to string data
Debug *Struct\s            ; now it displays
The best solution to this problem really depends on what you need to do with the string.

If you need to compare the string data to something else, the best thing
is to use commands like CompareMemoryString()/MemoryStringLength(),
which can operate on pointers, so you have no overhead at all.

If what you need is to copy the string from the pointer into a normal
PB variable, PeekS() is a good thing to use, as it is almost as fast as
a normak string copy like "a$ = b$".
quidquid Latine dictum sit altum videtur
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

I was a bit stupid actually, I found out that in reality I didn't need to cast anything at all, I could simply move the value into eax and then do a procedurereturn..... ....as soon as the debugger starts debugging strings correctly, of course:

Code: Select all

Procedure.s HardTrim(Instr.s) 
  Instr = Trim(Instr) 
  !mov eax, [esp] 
  !l_loopstart: 
  !cmp dword [eax], 0 
  !jz  l_hardtrim_procedurereturn 
  !inc eax 
  !jmp l_loopstart 
  !l_hardtrim_procedurereturn: 
  ProcedureReturn 
EndProcedure 

; Debug HardTrim("Debug statement messes it all up!") 
sine.s = HardTrim("Debug statement messes it up!") 
!mov dword [v_Temp], eax 
Debug PeekB(Temp) ; We see that this is a null string 
Debug sine        ; Which is why it debugs emptyness 
                  ; However, the infamous ... 
Debug HardTrim("Debug statement messes it all up!")
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

So the structureunion idea (2 posts earlier) is not a good one?
@}--`--,-- A rose by any other name ..
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

freak wrote:If what you need is to copy the string from the pointer into a normal PB variable, PeekS() is a good thing to use, as it is almost as fast as a normak string copy like "a$ = b$".
Not quite as fast:

Code: Select all

*AString.s = "Apple pie"

#Tries = 10000000 

; Fast way of copying a string from a pointer into a normal string
time = GetTickCount_() 
For I = 0 To #Tries 
  !mov  edx, [p_AString] 
  !lea  ecx, [v_InSane]
  !call SYS_FastAllocateStringFree
  InSane.s 
Next 
MessageRequester("", Str(GetTickCount_()-time)) 

; PeekS() way of copying a pointer into a normal string
time = GetTickCount_() 
For I = 0 To #Tries 
  InSane.s = PeekS(@AString) 
Next 
MessageRequester("", Str(GetTickCount_()-time))
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

Dare2 wrote:So the structureunion idea (2 posts earlier) is not a good one?
Not good for my purpose, since what I wanted to do was to return a pointer to a string as a string from a procedure. But instead of converting it to a string, then returning it, I forgot that I could simply leave the pointer in eax and case solved.Image
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

:D

Anyhow (and depending on use as, in some cases, you effectively have the string without a need to copy), even PeeKS is faster than this:

Code: Select all

Structure tooMany
  StructureUnion
    Zstring.string
    ptr.l
  EndStructureUnion
EndStructure

test.tooMany
test\ptr = AllocateMemory(20)
PokeS(test\ptr,"Peaches and Pears")

time = GetTickCount_()
For I = 0 To #Tries
  notSoMad.s = test\Zstring\s
Next
MessageRequester("", Str(GetTickCount_()-time))

FreeMemory(test\ptr)
Add some pointer arith and PeekS leaves it dead. And yours leaves PeeKS behind.

Case closed, as you said. :)

Edit: And thanks for the useful code.
@}--`--,-- A rose by any other name ..
freak
PureBasic Team
PureBasic Team
Posts: 5940
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

> Not quite as fast:

I said as fast as a normal "a$ = b$" statement, not as fast as some inline asm code.

try this:

Code: Select all

#LENGTH = 50000
#LOOPS  = 10000

String$ = RSet("A", #LENGTH, "A")
*Ptr = @String$
*Struct.String = @*Ptr

time0 = ElapsedMilliseconds()

For i = 1 To #LOOPS
  x$ = String$
Next i

time1 = ElapsedMilliseconds()

For i = 1 To #LOOPS
  x$ = *Struct\s
Next i

time2 = ElapsedMilliseconds()

For i = 1 To #LOOPS
  x$ = PeekS(*Ptr)
Next i

time3 = ElapsedMilliseconds()


Text$ = "x$ = String$:  "+Str(time1-time0)+" ms"+Chr(13)
Text$ + "x$ = *Struct\s:  "+Str(time2-time1)+" ms"+Chr(13)
Text$ + "x$ = PeekS(*Ptr):  "+Str(time3-time2)+" ms"+Chr(13)

MessageRequester("", Text$)
Try with different length and loop counts.. it makes no big difference.


Now your asm procedure above is compleetly wrong. Its a fluke that part of your code actually works.

Try to put the result from your procedure through another function, and you will see it fails as well:

Code: Select all

sine.s = Trim(HardTrim("Debug statement messes it up!"))
Have a look at the asm code generated by PB for a procedure that returns strings to
see how that is actually handled.

In fact, for returning a string from a procedure (given its pointer), PeekS() is probably the best way to go.

Code: Select all

Procedure.s A(Input$)
  ProcedureReturn Input$
EndProcedure

Procedure.s B(Input$)
  ProcedureReturn PeekS(@Input$)
EndProcedure

#LENGTH = 50000
#LOOPS  = 10000

String$ = RSet("A", #LENGTH, "A")

time0 = ElapsedMilliseconds()

For i = 1 To #LOOPS
  x$ = A(String$)
Next i

time1 = ElapsedMilliseconds()

For i = 1 To #LOOPS
  x$ = B(String$)
Next i

time2 = ElapsedMilliseconds()


Text$ = "A:  "+Str(time1-time0)+" ms"+Chr(13)
Text$ + "B:  "+Str(time2-time1)+" ms"

MessageRequester("", Text$)
Last edited by freak on Sun Mar 12, 2006 12:00 am, edited 1 time in total.
quidquid Latine dictum sit altum videtur
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

This is getting to be an interesting and informative/useful thread!


Edit:

If you don't actually need to copy the string (Say, for example, parsing an html file preloaded into memory), the structureunion approach with the .string structure embedded just flies. Need some nifty code to outperform it.
@}--`--,-- A rose by any other name ..
User avatar
Flype
Addict
Addict
Posts: 1542
Joined: Tue Jul 22, 2003 5:02 pm
Location: In a long distant galaxy

Post by Flype »

another trick based on freak tips.

parsing a string in memory :

Code: Select all

Macro LPSZSTR(userVar,userExpr)
  userVar#str.s = (userExpr)
  userVar#ptr.Byte = @userVar#str
  userVar.String = @userVar#ptr
EndMacro

LPSZSTR(*pointer,"TEMP = "+GetEnvironmentVariable("TEMP"))

While *pointerPtr\b
  Debug *pointer\s
  *pointerPtr + 1
Wend
No programming language is perfect. There is not even a single best language.
There are only languages well suited or perhaps poorly suited for particular purposes. Herbert Mayer
Trond
Always Here
Always Here
Posts: 7446
Joined: Mon Sep 22, 2003 6:45 pm
Location: Norway

Post by Trond »

freak wrote:> Not quite as fast:

I said as fast as a normal "a$ = b$" statement, not as fast as some inline asm code.
"Some inline assembly code" is exactly the same as PB generated for a$ = b$.

Code: Select all

; a$ = b$
  MOV    edx,dword [v_b$]
  LEA    ecx,[v_a$]
  CALL   SYS_FastAllocateStringFree
Dr. Dri
Enthusiast
Enthusiast
Posts: 243
Joined: Sat Aug 23, 2003 6:45 pm

Post by Dr. Dri »

i only read the first post of this topic and i would like to notice this:

@Variable.s gives the address of the string and not the address of the "Variable" symbol
There is no way you can get it with PB native (no asm) code

Maybe a new keyword could fix this (something like AddressOf(String.s) or whatever)

i wish i could do this with regular strings:

Code: Select all

Dim String.s(0)
String(0) = "PureBasic"

*Pointer.String = String()
Debug *Pointer
Debug @String(0)
Debug PeekL(*Pointer)
Debug *Pointer\s
Dri
Post Reply