Remake AdjustLineBreaks.

This version is correct and supposedly better in other ways (except for a bit of clarity maybe).
This commit is contained in:
Rika Ichinose 2023-04-08 07:13:34 +03:00 committed by FPK
parent 8ecdc6ed05
commit 1c4151d82e
2 changed files with 125 additions and 62 deletions

View File

@ -783,69 +783,60 @@ end;
function AdjustLineBreaks(const S: string; Style: TTextLineBreakStyle): string;
var
Source,Dest: PAnsiChar;
DestLen: Integer;
I,J,L: Longint;
Sp,Se,SLiteralStart,SLiteralEnd,Rp: PChar;
begin
Source:=Pointer(S);
L:=Length(S);
DestLen:=L;
I:=1;
while (I<=L) do
begin
case S[i] of
#10: if (Style=tlbsCRLF) then
Inc(DestLen);
#13: if (Style=tlbsCRLF) then
if (I<L) and (S[i+1]=#10) then
Inc(I)
else
Inc(DestLen)
else if (I<L) and (S[I+1]=#10) then
Dec(DestLen);
end;
Inc(I);
end;
if (DestLen=L) then
Result:=S
else
begin
SetLength(Result, DestLen);
FillChar(Result[1],DestLen,0);
Dest := Pointer(Result);
J:=0;
I:=0;
While I<L do
case Source[I] of
#10: begin
if Style=tlbsCRLF then
begin
Dest[j]:=#13;
Inc(J);
end;
Dest[J] := #10;
Inc(J);
Inc(I);
end;
#13: begin
if Style=tlbsCRLF then
begin
Dest[j] := #13;
Inc(J);
end;
Dest[j]:=#10;
Inc(J);
Inc(I);
if Source[I]=#10 then
Inc(I);
end;
else
Dest[j]:=Source[i];
Inc(J);
Inc(I);
end;
end;
Result:='';
repeat { Does two iterations, first is prepass, second fills the result with data and is distinguished by Assigned(Pointer(Result)). }
Rp:=Pointer(Result);
Sp:=PChar(S); { Readable #0 for empty string. }
Se:=Sp+Length(S);
SLiteralStart:=Sp;
repeat
while (Sp<Se) and not (Sp^ in [#13,#10]) do
Inc(Sp);
SLiteralEnd:=Sp; { Save position before consuming line ending. }
if Sp^=#10 then { These accesses rely on terminating #0. }
begin
Inc(Sp);
if Style=tlbsLF then
continue;
end
else if Sp^=#13 then
if Sp[1]=#10 then
begin
Inc(Sp,2);
if Style=tlbsCRLF then
continue;
end
else
begin
Inc(Sp);
if Style=tlbsCR then
continue;
end;
if Assigned(Pointer(Result)) then
Move(SLiteralStart^,Rp^,Pointer(SLiteralEnd)-Pointer(SLiteralStart)); { Byte difference to avoid signed div 2 on char = widechar. }
Inc(Pointer(Rp),Pointer(SLiteralEnd)-Pointer(SLiteralStart)); { Again, byte difference. }
if SLiteralEnd=Sp then
break;
SLiteralStart:=Sp;
Inc(Rp,1+ord(Style=tlbsCRLF));
if Assigned(Pointer(Result)) then
begin
if Style=tlbsCRLF then
Rp[-2]:=#13;
if Style=tlbsCR then
Rp[-1]:=#13
else
Rp[-1]:=#10;
end;
until false;
if Assigned(Pointer(Result)) then { Second pass finished. }
break;
if SLiteralStart=PChar(S) then { String is unchanged. }
Exit(S);
SetLength(Result,SizeUint(Pointer(Rp)-Pointer(Result)) div SizeOf(Char)); { Prepare second pass. }
until false;
end;

View File

@ -0,0 +1,72 @@
{$mode objfpc} {$longstrings on} {$coperators on}
uses
SysUtils;
var
somethingFailed: boolean = false;
function Repr(const s: string): string;
var
i: SizeInt;
begin
result := '';
for i := 1 to length(s) do
if (s[i] >= #32) and (s[i] <= #127) then
result += s[i]
else
result += '#' + IntToStr(ord(s[i])) +
specialize IfThen<string>((i < length(s)) and ((s[i] = #10) or (s[i] = #13) and (pChar(pointer(s))[i] <> #10)), LineEnding, '');
end;
procedure TestAdjustLineBreaks(const src: string; style: TTextLineBreakStyle; expect: string);
var
got, styleName: string;
begin
got := AdjustLineBreaks(src, style);
if got <> expect then
begin
WriteStr(styleName, style);
writeln('AdjustLineBreaks(' + LineEnding +
LineEnding +
Repr(src) + ',' + LineEnding +
LineEnding +
styleName + ')' + LineEnding +
LineEnding +
'=' + LineEnding +
LineEnding +
Repr(got) + LineEnding +
LineEnding +
'expected' + LineEnding +
LineEnding +
Repr(expect) + LineEnding);
somethingFailed := true;
end;
end;
const
D1 = 'Drinking the soup in the Dining Room will poison Viola and cause her to lose HP with each step.';
D2 = 'The Chef will chop Viola''s hands off if she chooses to lend the chef a hand in the Kitchen.';
D3 = 'Upon entering the Spider Room, If Viola takes the Butterfly without placing the Butterfly Model in the web, trying to exit the room will make a spider decapitate her.';
D4 = 'Reading the Book of Death will cause Viola to violently and uncontrollably bleed to death.';
D5 = 'Entering the Snake Room without feeding the Frog to the Snake will cause the Snake to eat Viola.';
D6 = 'If Viola visits the Frog Room after the Frog was killed, and Viola reads the note, a black hand will emerge from the black pit and grab her.';
LEs: array[TTextLineBreakStyle] of string = (#10, #13#10, #13);
var
style: TTextLineBreakStyle;
begin
for style in TTextLineBreakStyle do
begin
TestAdjustLineBreaks(
#10#13 + D1 + #13#10 + D2 + #10 + D3 + #13 + D4 + #13#10#10, style,
LEs[style] + LEs[style] + D1 + LEs[style] + D2 + LEs[style] + D3 + LEs[style] + D4 + LEs[style] + LEs[style]);
TestAdjustLineBreaks(
D5 + #13 + D6, style,
D5 + LEs[style] + D6);
end;
if somethingFailed then halt(1);
writeln('ok');
end.