Page 1 of 3

Find a string in *Memory

Posted: Fri Mar 29, 2024 7:21 pm
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.

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 7:29 pm
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 :?:

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 7:39 pm
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.

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 7:47 pm
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$

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 7:59 pm
by infratec
Optimized for unicode strings :wink:
+- 2 instead of +- 1

And removed the second condition in until. Use a break now.

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 7:59 pm
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)

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 8:06 pm
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)

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 8:19 pm
by infratec
Added time measurement with disabled debugger.

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 8:27 pm
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.

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 8:40 pm
by Caronte3D
AZJIO wrote: Fri Mar 29, 2024 8:27 pm Shouldn't DisableDebugger be placed at the beginning of the code?
Yes :wink:

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 8:45 pm
by infratec
Yep, corrected.

now it takes only 5ms :mrgreen:

Without debugger AZJIO needs 2ms

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 8:58 pm
by Caronte3D
Mine 11ms :? :lol: :lol:

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 9:20 pm
by infratec
Ok,

removed the CompareMemory() call.
Now I'm also at 2ms :mrgreen:

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 9:28 pm
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)

Re: Find a string in *Memory

Posted: Fri Mar 29, 2024 11:36 pm
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.