Find a string in *Memory

Just starting out? Need help? Post your questions and find answers here.
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Find a string in *Memory

Post by charvista »

Hello,
For string variables, we can find a word with FindString().
But how can we do that with *Memory ?

Note: do NOT attempt to put the contents in a string, as it can be very large and may contain null characters.

Code: Select all

HW.s = "Hello World"
*Mem = AllocateMemory(5000)
PokeS(*Mem+1820, HW+" of Europe !")
PokeS(*Mem+2605, HW+" of America!")
PokeS(*Mem+3675, HW+" of Asia   !")
PokeS(*Mem+3904, HW+" of Africa !")
PokeS(*Mem+4675, HW+" of Oceania!")
Here.i = FindMemoryString(*Mem, HW); this command or procedure does not exist, it must be created <<<<<<<<<<<
Debug PeekS(*Mem+Here+30, 7, #PB_Unicode); show the name of the continent
This is an example to know where I must PeekS() the name of the continent, as the "Hello World" 's are randomly hidden in the *Memory.
Also, it would be nice if I could find the string from the end of the *Memory block, to get directly the last one.
Thanks.
Last edited by charvista on Fri Mar 29, 2024 7:37 pm, edited 1 time in total.
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
infratec
Always Here
Always Here
Posts: 7625
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Find a string in *Memory

Post by infratec »

First:
I can not use your example .. my memory is not large enough.

Second:
You poke a unicode string and want to read it as ascii :?:
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Find a string in *Memory

Post by charvista »

@ infratec
Oops. You are right. I simplified the code in the first post for *Memory from 500000000 to 5000.
Also, yes, #PB_Unicode instead of #PB_Ascii.
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
infratec
Always Here
Always Here
Posts: 7625
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Find a string in *Memory

Post by infratec »

Code: Select all

EnableExplicit

DisableDebugger

Procedure.i FindStringInMemory(*Mem, StringToFind$, FindFromEnd.i=#False, ReturnOffset.i=#False, *StartAddress=#Null)
  
  Protected.i StringByteLength, Result
  Protected *MemEnd, *Ptr.Character, *FoundAt, *Ptr2.Character, *String.Character
  
  
  StringByteLength = StringByteLength(StringToFind$)
  *String = @StringToFind$
  
  If FindFromEnd
    
    If *StartAddress
      *Ptr = *StartAddress - StringByteLength
    Else
      *Ptr = *Mem + MemorySize(*Mem) - StringByteLength
    EndIf
    
    Repeat
      
      If *Ptr\c = *String\c
        *Ptr2 = *Ptr + 2
        *String + 2
        While *Ptr2\c = *String\c
          *Ptr2 + 2
          *String + 2
        Wend
        If *String\c = #Null
          *FoundAt = *Ptr
          Break
        EndIf
        *String = @StringToFind$
      EndIf
      
      *Ptr - 2
    Until *Ptr <= *Mem
    
  Else
    
    *MemEnd = *Mem + MemorySize(*Mem) - StringByteLength
    
    If *StartAddress
      *Ptr = *StartAddress
    Else
      *Ptr = *Mem
    EndIf
    
    Repeat
      
      If *Ptr\c = *String\c
        *Ptr2 = *Ptr + 2
        *String + 2
        While *Ptr2\c = *String\c
          *Ptr2 + 2
          *String + 2
        Wend
        If *String\c = #Null
          *FoundAt = *Ptr
          Break
        EndIf
        *String = @StringToFind$
      EndIf
      
      *Ptr + 2
    Until *Ptr >= *MemEnd
    
  EndIf
  
  If ReturnOffset
    Result = *FoundAt - *Mem
  Else
    Result = *FoundAt
  EndIf
  
  ProcedureReturn Result
  
EndProcedure




Define.q StartTime, EndTime
Define HW$, First$, Second$, Last$, NextToLast$
Define *Mem, *Here


HW$ = "Hello World of "
*Mem = AllocateMemory(5000000)
If *Mem
  Debug "Mem allocated"
  PokeS(*Mem + 1820540, HW$ + "Europe !")
  PokeS(*Mem + 2605545, HW$ + "America!")
  PokeS(*Mem + 3675082, HW$ + "Asia   !")
  PokeS(*Mem + 3904646, HW$ + "Africa !")
  PokeS(*Mem + 4675082, HW$ + "Oceania!")
  
  StartTime = ElapsedMilliseconds()
  *Here = FindStringInMemory(*Mem, HW$)
  EndTime = ElapsedMilliseconds()
  If *Here
    First$ = "First: " + PeekS(*Here + StringByteLength(HW$), 7) + " in " + Str(EndTime - StartTime) + "ms"
  EndIf
  
  StartTime = ElapsedMilliseconds()
  *Here = FindStringInMemory(*Mem, HW$, #False, #False, *Here + 2)
  EndTime = ElapsedMilliseconds()
  If *Here
    Second$ = "Second: " + PeekS(*Here + StringByteLength(HW$), 7) + " in " + Str(EndTime - StartTime) + "ms"
  EndIf
  
  StartTime = ElapsedMilliseconds()
  *Here = FindStringInMemory(*Mem, HW$, #True)
  EndTime = ElapsedMilliseconds()
  If *Here
    Last$ = "Last : " + PeekS(*Here + StringByteLength(HW$), 7) + " in " + Str(EndTime - StartTime) + "ms"
  EndIf
  
  StartTime = ElapsedMilliseconds()
  *Here = FindStringInMemory(*Mem, HW$, #True, #False, *Here)
  EndTime = ElapsedMilliseconds()
  If *Here
    NextToLast$ = "Next to last : " + PeekS(*Here + StringByteLength(HW$), 7) + " in " + Str(EndTime - StartTime) + "ms"
  EndIf
  
  FreeMemory(*Mem)
EndIf

EnableDebugger

Debug First$
Debug Second$
Debug Last$
Debug NextToLast$
Last edited by infratec on Sat Mar 30, 2024 2:32 pm, edited 11 times in total.
infratec
Always Here
Always Here
Posts: 7625
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Find a string in *Memory

Post by infratec »

Optimized for unicode strings :wink:
+- 2 instead of +- 1

And removed the second condition in until. Use a break now.
Last edited by infratec on Fri Mar 29, 2024 8:03 pm, edited 3 times in total.
AZJIO
Addict
Addict
Posts: 2193
Joined: Sun May 14, 2017 1:48 am

Re: Find a string in *Memory

Post by AZJIO »

https://www.purebasic.fr/english/viewtopic.php?t=81021

Code: Select all

EnableExplicit

Procedure FindMemoryString(*c.Character, *fc.Character, len2)
	Protected *FStart0, *Start0, i

	If Not *fc\c
		ProcedureReturn #False
	EndIf

	*FStart0 = *fc
	*Start0 = *c

	For i = 1 To len2
		While *c\c = *fc\c And i <= len2
			*fc + SizeOf(Character)
			*c + SizeOf(Character)
		Wend
		If *fc <> *FStart0
			If *fc\c
				*c - *fc + *FStart0
				*fc = *FStart0
			Else
				ProcedureReturn (*c - *Start0 - *fc + *FStart0) / SizeOf(Character)
			EndIf
		EndIf
		*c + SizeOf(Character)
	Next

	ProcedureReturn #False
EndProcedure

Define *mem, HW.s, Here
HW = "Hello World"
*mem = AllocateMemory(5000)
PokeS(*mem+1820, HW+" of Europe !")
PokeS(*mem+2605, HW+" of America!")
PokeS(*mem+3675, HW+" of Asia   !")
PokeS(*mem+3904, HW+" of Africa !")
PokeS(*mem+4675, HW+" of Oceania!")
Here = FindMemoryString(*mem, @HW, 5000)
Debug Here
Debug PeekS(*mem + (Here + 15) * SizeOf(Character), -1, #PB_Unicode)
User avatar
Caronte3D
Addict
Addict
Posts: 1362
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Find a string in *Memory

Post by Caronte3D »

Code: Select all

DisableDebugger

Procedure FindMemoryString(*Mem_, strFind_.s)
  Protected isEqual=-1
  Protected *MemFind_ = Ascii(strFind_)
  Protected lenStrFind = Len(strFind_)
  Protected mSize=MemorySize(*Mem_)
  Protected *offset=*Mem_
  While isEqual <> #PB_String_Equal And  offset < mSize
    isEqual = CompareMemoryString(*offset, *MemFind_, #PB_String_CaseSensitive, lenStrFind, #PB_Ascii )
    *offset+1
  Wend
  FreeMemory(*MemFind_)
  ProcedureReturn *offset-1-*Mem_
EndProcedure



HW.s = "Hello World"
*Mem = AllocateMemory(5000000)
PokeS(*Mem + 1820540, HW + " of Europe !",-1,#PB_Ascii)
PokeS(*Mem + 2605545, HW + " of America!",-1,#PB_Ascii)
PokeS(*Mem + 3675082, HW + " of Asia   !",-1,#PB_Ascii)
PokeS(*Mem + 3904646, HW + " of Africa !",-1,#PB_Ascii)
PokeS(*Mem + 4675082, HW + " of Oceania!",-1,#PB_Ascii)

StartTime = ElapsedMilliseconds()
Here.i = FindMemoryString(*Mem, HW); this command or procedure does not exist, it must be created <<<<<<<<<<<
EndTime = ElapsedMilliseconds()

  
EnableDebugger

Debug PeekS(*Mem + Here + 15, 7, #PB_Ascii); show the name of the continent
Debug Str(EndTime - StartTime) + "ms"

FreeMemory(*Mem)
Last edited by Caronte3D on Fri Mar 29, 2024 8:57 pm, edited 1 time in total.
infratec
Always Here
Always Here
Posts: 7625
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Find a string in *Memory

Post by infratec »

Added time measurement with disabled debugger.
AZJIO
Addict
Addict
Posts: 2193
Joined: Sun May 14, 2017 1:48 am

Re: Find a string in *Memory

Post by AZJIO »

infratec wrote: Fri Mar 29, 2024 8:19 pm Added time measurement with disabled debugger.
Shouldn't DisableDebugger be placed at the beginning of the code? To exclude debugging information inside a function.
User avatar
Caronte3D
Addict
Addict
Posts: 1362
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Find a string in *Memory

Post by Caronte3D »

AZJIO wrote: Fri Mar 29, 2024 8:27 pm Shouldn't DisableDebugger be placed at the beginning of the code?
Yes :wink:
infratec
Always Here
Always Here
Posts: 7625
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Find a string in *Memory

Post by infratec »

Yep, corrected.

now it takes only 5ms :mrgreen:

Without debugger AZJIO needs 2ms
User avatar
Caronte3D
Addict
Addict
Posts: 1362
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: Find a string in *Memory

Post by Caronte3D »

Mine 11ms :? :lol: :lol:
infratec
Always Here
Always Here
Posts: 7625
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Find a string in *Memory

Post by infratec »

Ok,

removed the CompareMemory() call.
Now I'm also at 2ms :mrgreen:
AZJIO
Addict
Addict
Posts: 2193
Joined: Sun May 14, 2017 1:48 am

Re: Find a string in *Memory

Post by AZJIO »

Test version, because it seems to me that I adjusted the code for the correct result.
It seemed to me that we should use bytes, then we can get the desired string using the Ascii() and UTF8() functions, and when reading data from a file, use the ReadData() function with any format.

Code: Select all

EnableExplicit
; DisableDebugger

Procedure FindMemoryStringLast(*Start, *StartF, len1, lenf2)
	Protected i, f, IsFound
	Protected *End.Byte
	Protected *EndF.Byte
	Protected *EndF0
	
	If Not *StartF
		ProcedureReturn #False
	EndIf

	*End = *Start + len1 - 1
	*EndF = *StartF + lenf2 - 1
	*EndF0 = *EndF

	For i = len1 To 1 Step -1
		IsFound = 1
		*EndF = *EndF0
		For f = lenf2 To 1 Step -1
			If *End\b = *EndF\b
				*EndF - 1
				*End - 1
			Else
				*End + lenf2 - f
				IsFound = 0
				Break
			EndIf
		Next
		If IsFound
			Break
		EndIf
		*End - 1
	Next


; 	If IsFound
; 		MessageRequester("Found", "|" + PeekS(*End + 1, 11, #PB_Unicode) + "|")
; 	EndIf

	ProcedureReturn *End + 1 - *Start
EndProcedure




Define *mem, HW.s, Here, StartTime
HW = "Hello World"
*mem = AllocateMemory(5000)
PokeS(*mem+1820, HW+" of Europe !")
PokeS(*mem+2605, HW+" of America!")
PokeS(*mem+3675, HW+" of Asia   !")
PokeS(*mem+3904, HW+" of Africa !")
PokeS(*mem+4675, HW+" of Oceania!")

StartTime = ElapsedMilliseconds()
Here = FindMemoryStringLast(*mem, @HW, 5000, StringByteLength(HW, #PB_Unicode))
StartTime = ElapsedMilliseconds() - StartTime
; EnableDebugger
Debug StartTime
Debug Here
Debug PeekS(*mem + (Here + 30), -1, #PB_Unicode)
User avatar
charvista
Addict
Addict
Posts: 949
Joined: Tue Sep 23, 2008 11:38 pm
Location: Belgium

Re: Find a string in *Memory

Post by charvista »

Interesting solutions!
I see that you are even trying to make it much faster! :D

Infratec's procedure is using *FoundAt as returning value. I changed this to *Ptr-*Mem so I can keep the position for further use on *Mem instead of *Ptr.
*FoundAt is then completely unnecessary, making the procedure even shorter. Of course the PeekS() is updated, too.
- Windows 11 Home 64-bit
- PureBasic 6.10 LTS (x64)
- 64 Gb RAM
- 13th Gen Intel(R) Core(TM) i9-13900K 3.00 GHz
- 5K monitor with DPI @ 200%
Post Reply