Freeing Strings

Just starting out? Need help? Post your questions and find answers here.
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Freeing Strings

Post by Xombie »

What's the generally accepted method for completely destroying the memory used by strings in a structure or elsewhere?

For example - this code ...

Code: Select all

Structure s_Test
   a.s
   b.l
EndStructure
*HoldTest.s_Test = AllocateMemory(SizeOf(s_Test))
*HoldTest\a = "Hello World"
*HoldTest\b = 1978
q.l = @*HoldTest\a
FreeMemory(*HoldTest)
Debug PeekS(q)
... how should I completely erase the memory used by 'q' ? And I mean completely erase, not just ...

Code: Select all

*HoldTest\a = ""
... unless that's the way to do it. Help?
User avatar
Psychophanta
Addict
Addict
Posts: 4997
Joined: Wed Jun 11, 2003 9:33 pm
Location: Lípetsk, Russian Federation
Contact:

Post by Psychophanta »

In the case (source) you have wrote, there is no doubt you really have freed the string buffer, because there is supposed the string is existent (allocated) by the program only in the case its pointer exists. If the string pointer is freed, the pointed string should automatically be vanished (i.e. its resources be freed) by the program (or by OS).
Keep in mind that even the contents of a mem buffer are not refilled with other data, it doesn't mean it has not been freed; i.e. FreeMemory() doesn´t fill freed heap with any data.

Besides i have seen now this:

Code: Select all

df$="1234567890"
Debug MemorySize(@df$)
Debug Len(df$)
As you can see, the buffer always reports 5 bytes bigger than the lenght of the string. Which means that there is allocated the size of the string (i.e. len()+1) plus a longword which is a pointer, i think.
So not sure, but it seems that you can free any string doing like this:

Code: Select all

FreeMemory(@df$)
This is the proof:

Code: Select all

df$="1234567890"
Debug df$
Debug MemorySize(@df$)
Debug Len(df$)

;FreeMemory(@df$); <- uncomment to see the effect

Debug df$
Debug MemorySize(@df$)
Debug Len(df$)
Last edited by Psychophanta on Thu Aug 10, 2006 9:55 am, edited 1 time in total.
http://www.zeitgeistmovie.com

While world=business:world+mafia:Wend
Will never leave this forum until the absolute bugfree PB :mrgreen:
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Post by helpy »

Windows 10 / Windows 7
PB Last Final / Last Beta Testing
User avatar
Psychophanta
Addict
Addict
Posts: 4997
Joined: Wed Jun 11, 2003 9:33 pm
Location: Lípetsk, Russian Federation
Contact:

Post by Psychophanta »

helpy wrote:Have a look at this topic: http://www.purebasic.fr/english/viewtopic.php?p=133623
Opps, then, if the memory allocated by strings (or whatever) inside a structure is no deallocated when freeing the variable of the mentioned structure, there is a big big big fault. :!:
http://www.zeitgeistmovie.com

While world=business:world+mafia:Wend
Will never leave this forum until the absolute bugfree PB :mrgreen:
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Post by helpy »

Psychophanta wrote:
helpy wrote:Have a look at this topic: http://www.purebasic.fr/english/viewtopic.php?p=133623
Opps, then, if the memory allocated by strings (or whatever) inside a structure is no deallocated when freeing the variable of the mentioned structure, there is a big big big fault. :!:
This happens only, if you dynamically create structures (AllocateMemory and pointer to structures). It does not happen with regular structured variables!
Windows 10 / Windows 7
PB Last Final / Last Beta Testing
User avatar
Psychophanta
Addict
Addict
Posts: 4997
Joined: Wed Jun 11, 2003 9:33 pm
Location: Lípetsk, Russian Federation
Contact:

Post by Psychophanta »

helpy wrote:This happens only, if you dynamically create structures (AllocateMemory and pointer to structures). It does not happen with regular structured variables!
Aha! then there are some more of logic :)
http://www.zeitgeistmovie.com

While world=business:world+mafia:Wend
Will never leave this forum until the absolute bugfree PB :mrgreen:
sverson
Enthusiast
Enthusiast
Posts: 284
Joined: Sun Jul 04, 2004 12:15 pm
Location: Germany

Re: Freeing Strings

Post by sverson »

Xombie wrote:What's the generally accepted method for completely destroying the memory used by strings in a structure or elsewhere?
...exactly what i wanted to ask today...

My solution:

Code: Select all

Structure MyTest
  *MyPointer
  MyLong.l
  MyQuad.q
  MyString.s
EndStructure

*MyMem.MyTest = AllocateMemory(SizeOf(MyTest))
If *MyMem
  *MyMem\MyPointer = *MyMem
  *MyMem\MyLong    = $7FFFFFFF
  *MyMem\MyQuad    = $7FFFFFFFFFFFFFFF
  *MyMem\MyString  = "*MyMem\MyString Test"

  ptrDebug.l = @*MyMem\MyString
  Debug "-----------------"
  Debug *MyMem\MyPointer
  Debug *MyMem\MyLong   
  Debug *MyMem\MyQuad   
  Debug @*MyMem\MyString 
  Debug "1: "+PeekS(ptrDebug)
  

  ; clear and free all allocated memory
  RtlZeroMemory_(@*MyMem\MyString,Len(*MyMem\MyString))
  FreeMemory(@*MyMem\MyString)
  RtlZeroMemory_(*MyMem,SizeOf(MyTest))
  FreeMemory(*MyMem)
  ; finished...

  Debug "-----------------"
  Debug *MyMem\MyPointer
  Debug *MyMem\MyLong   
  Debug *MyMem\MyQuad   
  Debug @*MyMem\MyString 
  Debug "2: "+PeekS(ptrDebug)
  Debug "-----------------"
  
EndIf
I hope this is the correct way...

;-) sverson

[EDIT] see also SecureZeroMemory. (http://msdn.microsoft.com/library/defau ... memory.asp)
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Post by helpy »

I tried your code in a loop WITHOUT debugger! 5 loops are ok, but in the 6th the program crashes.

Code: Select all

Structure MyTest
  *MyPointer
  MyLong.l
  MyQuad.q
  MyString.s
EndStructure

For i = 1 To 6
	*MyMem.MyTest = AllocateMemory(SizeOf(MyTest))
	If *MyMem
	  *MyMem\MyPointer = *MyMem
	  *MyMem\MyLong    = $7FFFFFFF
	  *MyMem\MyQuad    = $7FFFFFFFFFFFFFFF
	  *MyMem\MyString  = "*MyMem\MyString Test"
	
	  ptrDebug.l = @*MyMem\MyString
	  Debug "-----------------"
	  Debug *MyMem\MyPointer
	  Debug *MyMem\MyLong   
	  Debug *MyMem\MyQuad   
	  Debug @*MyMem\MyString
	  Debug "1: "+PeekS(ptrDebug)
	 
	
	  ; clear and free all allocated memory
	  RtlZeroMemory_(@*MyMem\MyString,Len(*MyMem\MyString))
	  FreeMemory(@*MyMem\MyString)
	  RtlZeroMemory_(*MyMem,SizeOf(MyTest))
	  FreeMemory(*MyMem)
	  ; finished...
	
	  Debug "-----------------"
	  Debug *MyMem\MyPointer
	  Debug *MyMem\MyLong   
	  Debug *MyMem\MyQuad   
	  Debug @*MyMem\MyString
	  Debug "2: "+PeekS(ptrDebug)
	  Debug "-----------------"
	 
	EndIf
Next i
Windows 10 / Windows 7
PB Last Final / Last Beta Testing
sverson
Enthusiast
Enthusiast
Posts: 284
Joined: Sun Jul 04, 2004 12:15 pm
Location: Germany

Post by sverson »

@helpy: same here!* :? (please take the above code as a question)

5..7 loops -> crash!

That's why i still look for a proper way to free strings inside structures.

FreeMemory(@*MyMem\MyString) does not work properly.

Your "workaround with a Macro" seems to be the only way ATM. :cry:
http://www.purebasic.fr/english/viewtopic.php?p=134805

@Fred:

Code: Select all

While famous_TODO_list[FreeStructureStrings]<top
  PrintN("please...") 
Wend
PrintN("Thanks ;-)") 
;-) sverson
Xombie
Addict
Addict
Posts: 898
Joined: Thu Jul 01, 2004 2:51 am
Location: Tacoma, WA
Contact:

Post by Xombie »

That's ... slightly worrisome. It means that nearly all of my code leaks memory like a sieve. I used the allocation of structures to create a kind of array/linked list hybrid and use it in a lot of my stuff. I'll see about using helpy's macro examble while waiting for something official. Thanks, helpy.

So another question would be - why does ...

Code: Select all

Structure s_test 
   a.s 
   b.l 
EndStructure 
*HoldTest.s_test = AllocateMemory(SizeOf(s_test)) 
*HoldTest\a = "Hello World" 
*HoldTest\b = 1978 
q.l = @*HoldTest\a 
FreeMemory(*HoldTest) 
Debug PeekS(q)
Debug *HoldTest\b
... return the 1978 for the second debug? If the memory is freed, the value should exist anywhere in memory, right?
freak
PureBasic Team
PureBasic Team
Posts: 5929
Joined: Fri Apr 25, 2003 5:21 pm
Location: Germany

Post by freak »

> ... return the 1978 for the second debug? If the memory is freed, the value should exist anywhere in memory, right?

By freeing the memory, you just tell the OS that you do not need it anymore. What the OS
then does with it cannot predicted.
The the memory (and its content) can remain for quite a while, or be invalidated
immediately. You may even receive the exact same pointer in a future AllocateMemory() call.

So if you have sensitive Information (like a password), overwriting it with 0 might be a good
thing before freeing the memory.

As for freeing the structured memory: Fred promised a function, but we are still working on
Linux, and with the Mac inbetween the next Update for Windows will be a while.

Using FreeMemory() on the PB strings is something you should do. Yes, i know it seems to work. (at least on some Windows versions)
The truth is though, that PB uses a different Memory heap for AllocateMemory and for the string management.
So theoretically, freeing the one thing with the function for the other (according to the API docs) should not work.
The Memory management of Windows is known to be very forgiving in this respect, so usually it works, yes.
I do not know if this is the case for all Windows versions though. Also the string handling on Linux/Mac is different, so it probably won't work there either.

Here is a very simple trick, that does the job quite nicely. It works on all OS, in PB 3.94 as well as 4.00 and probably will with future Versions as well.
The perfect function so to say ;). Its all about tricking PB into freein the String:

Code: Select all

Procedure FreePBString(*Address)
  Protected String.String  ; the String Structure contains one String element, which is initialized to 0 on procedure start
  PokeL(@String, *Address) ; poke our strings address into the structure
EndProcedure               ; when returning, PB's string free routine for the local structure will actually free the passed string.
Use it like this:

Code: Select all

Structure MyStrings
  a.s
  b.s
  c.s
EndStructure

Procedure test()
  Protected *x.MyStrings = AllocateMemory(SizeOf(MyStrings))
  
  *x\a = Space(4000)
  *x\b = Space(4000)
  *x\c = Space(4000)
  
  FreePBString(@*x\a)
  FreePBString(@*x\b)
  FreePBString(@*x\c)
  
  FreeMemory(*x)
EndProcedure

CallDebugger

For i = 0 To 10000
  test()
Next i

CallDebugger
quidquid Latine dictum sit altum videtur
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Post by helpy »

That is really nice! Thank you, freak!
Windows 10 / Windows 7
PB Last Final / Last Beta Testing
sverson
Enthusiast
Enthusiast
Posts: 284
Joined: Sun Jul 04, 2004 12:15 pm
Location: Germany

Post by sverson »

freak wrote:The perfect function so to say ;)
Great - thanks :)

After adding ZeroMemory it definitely is the perfect function!

Code: Select all

Procedure DeletePBString(*Address,delete.b=#False)
  Protected String.String                  ; the String Structure contains one String element, which is initialized to 0 on procedure start
  PokeL(@String, *Address)                 ; poke our strings address into the structure
  If delete
    RtlZeroMemory_(*Address,Len(String\s)) ; fill the String with zero 
  EndIf
EndProcedure                               ; when returning, PB's string free routine for the local structure will actually free the passed string.

Code: Select all

Structure MyStrings
  a.s
  b.s
  c.s
EndStructure

Procedure.l AvailPhysMem()
  Protected MemStat.MEMORYSTATUS
  GlobalMemoryStatus_(@MemStat)
  ProcedureReturn (MemStat\dwAvailPhys>>10)
EndProcedure

Procedure test()
  Protected *x.MyStrings = AllocateMemory(SizeOf(MyStrings))
  
  *x\a = Space(4000)
  *x\b = Space(4000)
  *x\c = Space(4000)
  
  ; DeletePBString(@*x\a,#True)
  ; DeletePBString(@*x\b,#True)
  ; DeletePBString(@*x\c,#True)
  
  FreeMemory(*x)
EndProcedure

Debug Str((4001*3*10000)>>10)+" Kb allocated!"

mem1.l = AvailPhysMem()
For i = 0 To 10000
  test()
Next i
mem2.l = AvailPhysMem()
Debug Str(mem1)+" Kb"
Debug Str(mem2)+" Kb"
Debug Str(mem1-mem2)+" Kb"
CallDebugger
Uncomment DeletePBString to to free and clear all strings.
There will remain just a little difference due to other programs allocate or free memory while the test is running.

;-) sverson
User avatar
Psychophanta
Addict
Addict
Posts: 4997
Joined: Wed Jun 11, 2003 9:33 pm
Location: Lípetsk, Russian Federation
Contact:

Post by Psychophanta »

freak wrote:

Code: Select all

Procedure FreePBString(*Address)
  Protected String.String  ; the String Structure contains one String element, which is initialized to 0 on procedure start
  PokeL(@String, *Address) ; poke our strings address into the structure
EndProcedure               ; when returning, PB's string free routine for the local structure will actually free the passed string.
:?:
I don't understand a thing :?
Can anybody explain that better? Or write the same code in ASM to undestand it?
http://www.zeitgeistmovie.com

While world=business:world+mafia:Wend
Will never leave this forum until the absolute bugfree PB :mrgreen:
User avatar
helpy
Enthusiast
Enthusiast
Posts: 552
Joined: Sat Jun 28, 2003 12:01 am

Post by helpy »

Psychophanta wrote:
freak wrote:

Code: Select all

Procedure FreePBString(*Address)
  Protected String.String  ; the String Structure contains one String element, which is initialized to 0 on procedure start
  PokeL(@String, *Address) ; poke our strings address into the structure
EndProcedure               ; when returning, PB's string free routine for the local structure will actually free the passed string.
:?:
I don't understand a thing :?
Can anybody explain that better? Or write the same code in ASM to undestand it?
"String.String" a local structure variable with ONE string element. All local structure variables are freed by PB when the procedure will be finished.

With "PokeL(@String, *Address) the passed string address is assigned to the structure variable. And know you have access to the string, which is identified by the passed string pointer, with the local structure variable String.String.

==> side effect: when the procedure is finished PB will free/release all local variables ... and also the local structure variables, which points to the passed string pointer.

Hope you understand a german native speaker ;-)

cu, helpy
Windows 10 / Windows 7
PB Last Final / Last Beta Testing
Post Reply