Recent

Author Topic: Why this code is working ?  (Read 550 times)

Tommi

  • Sr. Member
  • ****
  • Posts: 256
Why this code is working ?
« on: December 04, 2025, 09:13:51 am »
Yesterday I wrote this simple code, but today I realize that I did a mistake. Despite this, the code is working. In few words I pass to stringReplace the string S and not a pointer to S. So S into stringReplace should be a copy and not the original.
So now I don't understand why it works.
My goal was simply to speed up stringReplace without force to allocate new RAM anytime.

Code: [Select]
procedure stringReplace(s,sOld,sNew:string);
var
  i,t,m,n:integer;
  a,old,new:PByte;
  lengthOrig,lengthOld,lengthNew:integer;
begin
  a:=pointer(s);
  lengthOrig:=length(s);
  old:=pointer(sOld);
  new:=pointer(sNew);
  lengthOld:=length(sOld);
  lengthNew:=length(sNew);
  n:=lengthNew-lengthOld;
  if lengthOld=0 then exit;
  if n<=0 then
  begin
    i:=0;
    while a[i]<>0 do
    begin
      m:=0;
      for t:=0 to lengthOld-1 do
      begin
        if a[i+t]<>0 then
        begin
          if a[i+t]=old[t] then inc(m) else break;
        end;
      end;
      if (m=lengthOld) then
      begin
        for t:=0 to lengthNew-1 do
        begin
          a[i+t]:=new[t];
        end;
        if (lengthNew<lengthOld) then
        begin
          move(a[i+t+1-n],a[i+t+1],lengthOrig-(i+t-n));
          lengthOrig:=length(s);
        end;
        i:=i+t;
      end;
      inc(i);
    end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  a,i:integer;
  b,c,d:string;
  p,old,new:PByte;
begin
  b:=edit1.Text;
  c:='CIAO';
  d:='H';
  stringReplace(b,c,d);
  form1.Caption:=b;
end;
[/quote]

nikel

  • Sr. Member
  • ****
  • Posts: 267
Re: Why this code is working ?
« Reply #1 on: December 04, 2025, 09:34:17 am »
I couldn't carefully read your code but it's not adviced to mess your code with ppointers, because they're already pointers.

Thausand

  • Sr. Member
  • ****
  • Posts: 457
Re: Why this code is working ?
« Reply #2 on: December 04, 2025, 10:41:01 am »
So now I don't understand why it works.
Code have use pointer. Code that make pointer is highlight.

I have guess and you have make solution for code because edit1.text is property and is not possible when use property pointer for make refer string.

Code: Pascal  [Select][+][-]
  1. procedure stringReplace(s,sOld,sNew:string);
  2. var
  3.   i,t,m,n:integer;
  4.   a,old,new:PByte;
  5.   lengthOrig,lengthOld,lengthNew:integer;
  6. begin
  7.   a:=pointer(s);
  8.   lengthOrig:=length(s);
  9.   old:=pointer(sOld);
  10.   new:=pointer(sNew);
  11.   lengthOld:=length(sOld);
  12.   lengthNew:=length(sNew);
  13.   n:=lengthNew-lengthOld;
  14.   if lengthOld=0 then exit;
  15.   if n<=0 then
  16.   begin
  17.     i:=0;
  18.     while a[i]<>0 do
  19.     begin
  20.       m:=0;
  21.       for t:=0 to lengthOld-1 do
  22.       begin
  23.         if a[i+t]<>0 then
  24.         begin
  25.           if a[i+t]=old[t] then inc(m) else break;
  26.         end;
  27.       end;
  28.       if (m=lengthOld) then
  29.       begin
  30.         for t:=0 to lengthNew-1 do
  31.         begin
  32.           a[i+t]:=new[t];
  33.         end;
  34.         if (lengthNew<lengthOld) then
  35.         begin
  36.           move(a[i+t+1-n],a[i+t+1],lengthOrig-(i+t-n));
  37.           lengthOrig:=length(s);
  38.         end;
  39.         i:=i+t;
  40.       end;
  41.       inc(i);
  42.     end;
  43.   end;
  44. end;
  45.  
  46. procedure TForm1.Button1Click(Sender: TObject);
  47. var
  48.   a,i:integer;
  49.   b,c,d:string;
  50.   p,old,new:PByte;
  51. begin
  52.   b:=edit1.Text;
  53.   c:='CIAO';
  54.   d:='H';
  55.   stringReplace(b,c,d);
  56.   form1.Caption:=b;
  57. end;
  58.  

PS: code is not work. and give error "general protection fault" and that good because when use pointer cast is write parameter memory and not have access. For fix have use var parameter. But that no work for property so the have function return string.
« Last Edit: December 04, 2025, 10:42:33 am by Thausand »

Tommi

  • Sr. Member
  • ****
  • Posts: 256
Re: Why this code is working ?
« Reply #3 on: December 04, 2025, 10:48:58 am »
But I am passing B, not edit1.text.
And compiling (tested with lazarus 4.0) it works, this is the strange fact.

Anyway I just tried to port the same code to console program and it crashes.

Tommi

  • Sr. Member
  • ****
  • Posts: 256
Re: Why this code is working ?
« Reply #4 on: December 04, 2025, 10:52:10 am »
I couldn't carefully read your code but it's not adviced to mess your code with ppointers, because they're already pointers.

The problem is that if I write:
a:=stringReplace(a,...)
or
b:=stringReplace(a,...)

The compiler compiles in the same way, but in the first case (if the new string is shorter then the old) it isn't necessary allocate new memory wasting time.

cdbc

  • Hero Member
  • *****
  • Posts: 2571
    • http://www.cdbc.dk
Re: Why this code is working ?
« Reply #5 on: December 04, 2025, 10:53:15 am »
Hi
·) 'b' gets assigned a copy of the text in 'Edit1', 'b' is, under the hood, a
    pointer to the text allocated on the heap.
·) You send in 'b' as a parameter, no /const/, ie.: a copy, which means it
    increments the reference-count on the original 'b', and points to the same
    address.
·) Then you make 'a' point to the address of 'S', which in turn points to 'b',
    making @'a' = @'b' in your procedure.
·) In your procedure, you work on 'a' -- That's why it works for you.
·) If you change 'S' in your procedure, before you assign its address to 'a',
    then it won't work -- "Copy on Write" mechanism.
HTH
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

Tommi

  • Sr. Member
  • ****
  • Posts: 256
Re: Why this code is working ?
« Reply #6 on: December 04, 2025, 10:57:10 am »
Hi
·) 'b' gets assigned a copy of the text in 'Edit1', 'b' is, under the hood, a
    pointer to the text allocated on the heap.
·) You send in 'b' as a parameter, no /const/, ie.: a copy, which means it
    increments the reference-count on the original 'b', and points to the same
    address.
·) Then you make 'a' point to the address of 'S', which in turn points to 'b',
    making @'a' = @'b' in your procedure.
·) In your procedure, you work on 'a' -- That's why it works for you.
·) If you change 'S' in your procedure, before you assign its address to 'a',
    then it won't work -- "Copy on Write" mechanism.
HTH
Regards Benny

Ok, thank you. Now it is clear to me :)

cdbc

  • Hero Member
  • *****
  • Posts: 2571
    • http://www.cdbc.dk
Re: Why this code is working ?
« Reply #7 on: December 04, 2025, 11:02:14 am »
Hi
No worries mate  ;D
Try changing the 'S' param to 'ShortString' and it crashes immediately, because a 'ShortString' is a value-type, not a pointer-type...
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE6/QT6 -> FPC Release -> Lazarus Release &  FPC Main -> Lazarus Main

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 12019
  • Debugger - SynEdit - and more
    • wiki
Re: Why this code is working ?
« Reply #8 on: December 04, 2025, 11:07:08 am »
I haven't read all the code, but

without force to allocate new RAM anytime.

You can't completely avoid that.

Strings are refcounted, and "copy on write"
That means:
Code: Pascal  [Select][+][-]
  1. var a,b: AnsiString; // string in {$H+}
  2. begin
  3.   a := randomString();
  4.   b := a;
  5.  

There is only ONE copy of the text. a and b both point to the same memory. (a and b both have a refcount of 2 / maybe even more, as their may be temp refcounts)

If you do, after the above
Code: Pascal  [Select][+][-]
  1. b[1] := 'a';
then that triggers "copy on write" => you write to the string, and it is currently sharing its memory, so you need a new copy for "b". This makes sure that a is not modified.

But if instead you do
Code: Pascal  [Select][+][-]
  1. b := a;
  2. p := pchar(b); // or any pointer type
  3. p[0] := 'a';
Then that does NOT trigger copy on write.
This will modify both (a and b).
And that is not what you want.

Before you modify a string, using any kind of pointer like that, you must call UniqueString
Code: Pascal  [Select][+][-]
  1. b := a;
  2. UniqueString(b); // before taking the pointer, because this may change the mem location, and any pointer you already have may be wrong after this.
  3. p := pchar(b); // or any pointer type
  4. p[0] := 'a';


So what you want to do is.
- using a pointer search for the first char that needs to be replaced
- if there is nothing to be replaced, then exit
- compute the distance/offset of that pointer from the state (index of the first char that needs to be replaced)
- call UniqueString // if the string may grow, then use SetLength instead SetLength also always makes the string unique
- take a new pointer to the string => then add the index, so you are again at the first char to be replaced
- do the replacing



I am pretty sure some such replace function already exists. StringReplace or TextReplace or similar.
You need to find the one that does not deal with upper/lower casing...

Also, why PByte and not PChar?
MyPchar := PChar(MyString);


Tommi

  • Sr. Member
  • ****
  • Posts: 256
Re: Why this code is working ?
« Reply #9 on: December 05, 2025, 09:53:10 am »
OK, thank you very much for your help. I didn't know copy-on-write mechanism.

 

TinyPortal © 2005-2018