Recent

Author Topic: Find text in memo gives memory leak  (Read 529 times)

Hansvb

  • Sr. Member
  • ****
  • Posts: 473
Find text in memo gives memory leak
« on: March 28, 2023, 04:16:29 pm »
Hi,

On this website
Quote
https://wiki.freepascal.org/TMemo
I found how to search for a text in a memo. I copied this almost exactly. Searching for a text in the memo works fine but when I close the app I have a memory leak. If I remove or don't use the search function then I don't have a memory leak.
However, I don't see what could be causing a memory leak in this function.
what goes wrong here?

Code: Pascal  [Select][+][-]
  1. procedure TFrmMain.ButtonSearchClick(Sender: TObject);
  2. const
  3.   SearchStr : String = '';
  4.   SearchStart : Integer = 0;
  5. begin
  6.   // heeft een geheugenlek... ???
  7.   if SearchStr <> EditSearchFormatedMemo.Text then begin
  8.     SearchStart := 0;
  9.     SearchStr := EditSearchFormatedMemo.Text;
  10.   end;
  11.  
  12.   SearchStart := FindInMemo(MemoFormatted, SearchStr, SearchStart + 1);
  13.   if SearchStart > 0 then begin
  14.     Caption := 'Found at position[' + IntToStr(SearchStart) + ']!';
  15.   end
  16.   else begin
  17.     Caption := 'No further finds.';
  18.   end;
  19. end;
  20.  
  21. function TFrmMain.FindInMemo(aMemo: TMemo; aString: String; StartPos: Integer
  22.   ): Integer;
  23. begin
  24.   // UpperCase. niet hoofdleter gevoelig voorlopig.
  25.   Result := PosEx(UpperCase(aString), UpperCase(aMemo.Text), StartPos);
  26.   //Result := PosEx(aString, aMemo.Text, StartPos);
  27.   if Result > 0 then begin
  28.     aMemo.SelStart := UTF8Length(PChar(aMemo.Text), Result -1);
  29.     aMemo.SelLength := Length(aString);
  30.     aMemo.SetFocus;
  31.   end;
  32. end;

howardpc

  • Hero Member
  • *****
  • Posts: 4138
Re: Find text in memo gives memory leak
« Reply #1 on: March 28, 2023, 08:03:52 pm »
There seems to be a minor bug in LazFileUtils, specifically in the

Code: Pascal  [Select][+][-]
  1. function ConvertUTF16ToUTF8(Dest: PChar; DestCharCount: SizeUInt;
  2.   Src: PWideChar; SrcWideCharCount: SizeUInt; Options: TConvertOptions;
  3.   out ActualCharCount: SizeUInt): TConvertResult;
althouugh I am not sure I have traced through the debugger correctly.
The function is very clever, and relies on casts of string to PChar, and also casts the
Code: Pascal  [Select][+][-]
  1. SrcI
'th widechar to a Word value which is used in a comparison. Perhaps the output string is not finalised correctly?
I am not clever enough to see what is causing the leak, but ansistring/widestring conversion is a pretty specialist topic, and I am sure it requires an expert in cross-platform string handling to get it right, since all strings (except shortstrings) have automatic memory management by the compiler. So things happen "behind your back", and in this instance perhaps the timing of the auto-memory managment works against the logic of the function?
There is no bug in the code you posted that I can see.

dsiders

  • Hero Member
  • *****
  • Posts: 870
Re: Find text in memo gives memory leak
« Reply #2 on: March 28, 2023, 08:55:19 pm »
There seems to be a minor bug in LazFileUtils, specifically in the
Code: Pascal  [Select][+][-]
  1. function ConvertUTF16ToUTF8(Dest: PChar; DestCharCount: SizeUInt;
  2.   Src: PWideChar; SrcWideCharCount: SizeUInt; Options: TConvertOptions;
  3.   out ActualCharCount: SizeUInt): TConvertResult;

Did you post a bug report?
Preview Lazarus 2.3.0 documentation at: https://dsiders.gitlab.io/lazdocsnext

howardpc

  • Hero Member
  • *****
  • Posts: 4138
Re: Find text in memo gives memory leak
« Reply #3 on: March 28, 2023, 09:04:00 pm »
No because I cannot accurately pinpoint where the bug lies, or even if it is in that particular function.

Bart

  • Hero Member
  • *****
  • Posts: 4971
    • Bart en Mariska's Webstek
Re: Find text in memo gives memory leak
« Reply #4 on: March 28, 2023, 10:38:04 pm »
Confirmed with more simple program:
Code: Pascal  [Select][+][-]
  1. program test;
  2. {$mode objfpc}
  3. {$h+}
  4.  
  5. uses
  6.   Classes;
  7. var
  8.   SL: TStringList;
  9. procedure Foo;
  10. const
  11.   S: String = '';
  12. begin
  13.   S := SL.Text;
  14. end;
  15. begin
  16.   SL := TStringList.Create;
  17.   SL.Add('1');
  18.   Foo;
  19.   SL.Free;
  20. end.
Code: [Select]
C:\Users\Bart\LazarusProjecten>ConsoleProjecten\test
Heap dump by heaptrc unit of C:\Users\Bart\LazarusProjecten\ConsoleProjecten\test.exe
116 memory blocks allocated : 2817/3104
115 memory blocks freed     : 2801/3088
1 unfreed memory blocks : 16
True heap size : 262144 (112 used in System startup)
True free heap : 261904
Should be : 261920
Call trace for block $015E1338 size 16
  $00401768  Foo,  line 13 of test.pas
  $004017F3  $main,  line 18 of test.pas

Bart

paweld

  • Hero Member
  • *****
  • Posts: 673
Re: Find text in memo gives memory leak
« Reply #5 on: March 28, 2023, 10:38:39 pm »
Change const to var and no memory leaks. 
Code: Pascal  [Select][+][-]
  1. procedure TFrmMain.ButtonSearchClick(Sender: TObject);
  2. var  //<-- here
  3.   SearchStr: String = '';
  4.   SearchStart: Integer = 0;
  5. begin
  6.   // heeft een geheugenlek... ???
  7.   if SearchStr <> EditSearchFormatedMemo.Text then
  8.   begin
  9.     SearchStart := 0;
  10.     SearchStr := EditSearchFormatedMemo.Text;
  11.   end;
  12.  
  13.   SearchStart := FindInMemo(MemoFormatted, SearchStr, SearchStart + 1);
  14.   if SearchStart > 0 then
  15.   begin
  16.     Caption := 'Found at position[' + IntToStr(SearchStart) + ']!';
  17.   end
  18.   else
  19.   begin
  20.     Caption := 'No further finds.';
  21.   end;
  22. end;
  23.  
  24. function TFrmMain.FindInMemo(aMemo: TMemo; aString: String; StartPos: Integer): Integer;
  25. begin
  26.   // UpperCase. niet hoofdleter gevoelig voorlopig.
  27.   Result := PosEx(UpperCase(aString), UpperCase(aMemo.Text), StartPos);
  28.   //Result := PosEx(aString, aMemo.Text, StartPos);
  29.   if Result > 0 then
  30.   begin
  31.     aMemo.SelStart := UTF8Length(PChar(aMemo.Text), Result - 1);
  32.     aMemo.SelLength := Length(aString);
  33.     aMemo.SetFocus;
  34.   end;
  35. end;  
  36.  
 
or clear SearchStr and SearchStart at the end of the procedure:
Code: Pascal  [Select][+][-]
  1. procedure TFrmMain.ButtonSearchClick(Sender: TObject);
  2. const
  3.   SearchStr: String = '';
  4.   SearchStart: Integer = 0;
  5. begin
  6.   // heeft een geheugenlek... ???
  7.   if SearchStr <> EditSearchFormatedMemo.Text then
  8.   begin
  9.     SearchStart := 0;
  10.     SearchStr := EditSearchFormatedMemo.Text;
  11.   end;
  12.  
  13.   SearchStart := FindInMemo(MemoFormatted, SearchStr, SearchStart + 1);
  14.   if SearchStart > 0 then
  15.   begin
  16.     Caption := 'Found at position[' + IntToStr(SearchStart) + ']!';
  17.   end
  18.   else
  19.   begin
  20.     Caption := 'No further finds.';
  21.   end;
  22.   SearchStr := '';       //<-- here
  23.   SearchStart := 0;   //<-- and here
  24. end;            
  25.  
« Last Edit: March 28, 2023, 10:42:31 pm by paweld »
Best regards / Pozdrawiam
paweld

Bart

  • Hero Member
  • *****
  • Posts: 4971
    • Bart en Mariska's Webstek
Re: Find text in memo gives memory leak
« Reply #6 on: March 28, 2023, 10:44:13 pm »
He uses const, so the value is preserved when leaving and re-entering the procedure.
And yes, making it a var, or making it a shortstring resolves the memory leak.

I asked on the fpc devel mailinglist.

Bart

cdbc

  • Sr. Member
  • ****
  • Posts: 492
    • http://www.cdbc.dk
Re: Find text in memo gives memory leak
« Reply #7 on: March 29, 2023, 12:03:34 pm »
Hi
2 alternatives spring to mind...
1) UTF8Pos from unit lazutf8.
2) Function FindMatchesBoyerMooreCaseInSensitive(const S, OldPattern: String; out aMatches: SizeIntArray; const aMatchAll: Boolean) : Boolean; from unit StrUtils.
I've written my own, which is a mixture of the 2 above, works flawless with utf8 et.al.
It is a rather lengthy function, but let me know if you need it  ;)
Regards Benny
If it ain't broke, don't fix it ;)

Bart

  • Hero Member
  • *****
  • Posts: 4971
    • Bart en Mariska's Webstek
Re: Find text in memo gives memory leak
« Reply #8 on: March 30, 2023, 12:09:53 am »
Even simpler example of triggering the memory leak:
Code: Pascal  [Select][+][-]
  1. program test;
  2. {$mode objfpc}
  3. {$h+}
  4.  
  5. var
  6.   HideTheMemoryLeak: Boolean;
  7.  
  8. procedure Foo;
  9. const
  10.   S: String = '';
  11. begin
  12.   S := 'X';
  13.   if HideTheMemoryLeak then Exit;
  14.   S := S + 'Y';  // This line cause a memory leak
  15. end;
  16.  
  17. begin
  18.   HideTheMemoryLeak := (ParamCount = 1) and (LowerCase(ParamStr(1)) = 'hidethememoryleak');
  19.   Foo;
  20. end.

Reported in the bugtracker

 

TinyPortal © 2005-2018