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

+- 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

Re: Find a string in *Memory
Posted: Fri Mar 29, 2024 8:45 pm
by infratec
Yep, corrected.
now it takes only 5ms
Without debugger AZJIO needs 2ms
Re: Find a string in *Memory
Posted: Fri Mar 29, 2024 8:58 pm
by Caronte3D
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

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!
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.