diff --git a/ide/packages/idedebugger/idedebuggerwatchresprinter.pas b/ide/packages/idedebugger/idedebuggerwatchresprinter.pas index e8eaf6f35d..3ef04274ef 100644 --- a/ide/packages/idedebugger/idedebuggerwatchresprinter.pas +++ b/ide/packages/idedebugger/idedebuggerwatchresprinter.pas @@ -7,8 +7,8 @@ interface uses Classes, SysUtils, Math, IdeDebuggerWatchResult, IdeDebuggerUtils, IdeDebuggerDisplayFormats, - IdeDebuggerBase, IdeDebuggerStringConstants, IdeDebuggerValueFormatter, LazDebuggerIntf, LazUTF8, - IdeDebuggerWatchValueIntf, StrUtils, LazDebuggerUtils; + IdeDebuggerBase, IdeDebuggerStringConstants, IdeDebuggerValueFormatter, IdeDebuggerWatchResUtils, + LazDebuggerIntf, LazUTF8, IdeDebuggerWatchValueIntf, StrUtils, LazDebuggerUtils; type @@ -103,12 +103,12 @@ type const ANumFormat: TResolvedDisplayFormatNum; PrintNil: Boolean = False ): String; - function PrintArray(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): String; - function PrintStruct(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): String; - function PrintConverted(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): String; - function PrintProc(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer): String; + function PrintArray(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): TStringBuilderPart; + function PrintStruct(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): TStringBuilderPart; + function PrintConverted(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): TStringBuilderPart; + function PrintProc(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer): TStringBuilderPart; - function PrintWatchValueEx(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): String; + function PrintWatchValueEx(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): TStringBuilderPart; public constructor Create; destructor Destroy; override; @@ -601,7 +601,8 @@ begin end; function TWatchResultPrinter.PrintArray(AResValue: TWatchResultData; - const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): String; + const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String + ): TStringBuilderPart; type TIntegerArray = array of integer; @@ -695,11 +696,11 @@ var CouldHide, OldCurrentArrayLenShown, LoopCurrentArrayLenShown, CouldSingleLine, ShowMultiLine: Boolean; MultiLine: TWatchDisplayFormatMultiline; - Results: array of string; PrefixIdxList: TIntegerArray; LenPrefix: TWatchDisplayFormatArrayLen; ArrayTypes: TLzDbgArrayTypes; EntryVal: TWatchResultData; + R2: PString; begin inc(FCurrentMultilineLvl); if (ANestLvl > FDeepestArray) then @@ -709,17 +710,18 @@ begin tn := AResValue.TypeName; if (AResValue.Count = 0) and (AResValue.DataAddress = 0) then begin if (ADispFormat.Struct.DataFormat = vdfStructFull) then - Result := AResValue.TypeName + '(nil)' + Result.RawAsStringPtr^ := AResValue.TypeName + '(nil)' else - Result := 'nil'; + Result.RawAsString := 'nil'; exit; end; if (ADispFormat.Struct.ShowPointerFormat = vdfStructPointerOnly) then begin - Result := '$'+IntToHex(AResValue.DataAddress, HexDigicCount(AResValue.DataAddress, 4, True)); + R2 := Result.RawAsStringPtr; + R2^ := '$'+IntToHex(AResValue.DataAddress, HexDigicCount(AResValue.DataAddress, 4, True)); if tn <> '' then - Result := tn + '(' + Result + ')'; + R2^ := tn + '(' + R2^ + ')'; exit; end; end; @@ -781,16 +783,6 @@ begin ArrayTypes := [Low(TLzDbgArrayType)..High(TLzDbgArrayType)]; end; - - - Cnt := AResValue.Count; - CutOff := (Cnt < AResValue.ArrayLength); - if CutOff then begin - SetLength(Results, Cnt+1); - end - else - SetLength(Results, Cnt); - if ShowLen then begin if ShowCombined then begin CheckArrayIndexes(AResValue, PrefixIdxList, PrefixIdxCnt, ArrayTypes); @@ -813,106 +805,115 @@ begin LenStr := Format(drsLen2, [LenStr]); end; end; + CouldHide := LenPrefix.HideLen and (AResValue.ArrayLength <= LenPrefix.HideLenThresholdCnt); + + if AResValue.ArrayLength = 0 then begin + if ShowLen and not CouldHide then + Result.RawAsStringPtr^ := LenStr + '()' + else + Result.RawAsString := '()'; + exit; + end; + + Cnt := AResValue.Count; + CutOff := (Cnt < AResValue.ArrayLength); + if CutOff then begin + Result.RawPartCount := Cnt + 1; + end + else + Result.RawPartCount := Cnt; LoopCurrentArrayLenShown := FCurrentArrayLenShown; FCurrentArrayLenShown := False; - CouldHide := LenPrefix.HideLen and (AResValue.ArrayLength <= LenPrefix.HideLenThresholdCnt); HideLenEach := Max(1, LenPrefix.HideLenThresholdEach); MaxLen := 1000*1000 div Max(1, ANestLvl*4); Len := 0; - if Cnt > 0 then begin + if Cnt > 0 then dec(FElementCount); - for i := 0 to Cnt - 1 do begin - AResValue.SetSelectedIndex(i); - ElemCnt := FElementCount; - EntryVal := AResValue.SelectedEntry; - Results[i] := PrintWatchValueEx(EntryVal, ADispFormat, ANestLvl, AWatchedExpr); - Len := Len + Length(Results[i]) + SepLen; + for i := 0 to Cnt - 1 do begin + AResValue.SetSelectedIndex(i); + ElemCnt := FElementCount; + EntryVal := AResValue.SelectedEntry; + Result.RawParts[i] := PrintWatchValueEx(EntryVal, ADispFormat, ANestLvl, AWatchedExpr); + Len := Len + Result.PartsTotalLen[i] + SepLen; - if CouldHide and ( - ( (LenPrefix.HideLenThresholdLen > 0) and - (Length(Results[i]) > LenPrefix.HideLenThresholdLen) - ) or - (FElementCount - ElemCnt > HideLenEach) or - ( (LenPrefix.HideLenThresholdEach = 0) and (EntryVal.ValueKind in [rdkArray, rdkStruct]) ) - ) - then - CouldHide := False; - - if CouldSingleLine and ( - ( (MultiLine.ForceSingleLineThresholdLen > 0) and - (Length(Results[i]) > MultiLine.ForceSingleLineThresholdLen) - ) or - (FElementCount - ElemCnt > ForceSingleLineEach) or - ( (MultiLine.ForceSingleLineThresholdEach = 0) and (EntryVal.ValueKind in [rdkArray, rdkStruct]) ) - ) - then - CouldSingleLine := False; - - if Len > MaxLen then begin - CutOff := True; - Cnt := i+1; - SetLength(Results, Cnt+1); - end; - end; - - if FCurrentArrayLenShown then + if CouldHide and ( + ( (LenPrefix.HideLenThresholdLen > 0) and + (Result.PartsTotalLen[i] > LenPrefix.HideLenThresholdLen) + ) or + (FElementCount - ElemCnt > HideLenEach) or + ( (LenPrefix.HideLenThresholdEach = 0) and (EntryVal.ValueKind in [rdkArray, rdkStruct]) ) + ) + then CouldHide := False; - FCurrentArrayLenShown := FCurrentArrayLenShown or LoopCurrentArrayLenShown; - if FHasLineBreak or - ( ( (ANestLvl < FDeepestArray) or (MultiLine.ForceSingleLineReverseDepth <= 1) ) and - (AResValue.FieldCount > MultiLine.ForceSingleLineThresholdStructFld) ) or - (Min(FDeepestMultilineLvl, MultiLine.MaxMultiLineDepth) - FCurrentMultilineLvl >= MultiLine.ForceSingleLineReverseDepth) + if CouldSingleLine and ( + ( (MultiLine.ForceSingleLineThresholdLen > 0) and + (Result.PartsTotalLen[i] > MultiLine.ForceSingleLineThresholdLen) + ) or + (FElementCount - ElemCnt > ForceSingleLineEach) or + ( (MultiLine.ForceSingleLineThresholdEach = 0) and (EntryVal.ValueKind in [rdkArray, rdkStruct]) ) + ) then CouldSingleLine := False; - if CutOff then begin - Results[Cnt] := '...'; - inc(Cnt); + if Len > MaxLen then begin + CutOff := True; + Cnt := i+1; + Result.RawChangePartCount(Cnt+1); end; + end; - if ShowMultiLine and not CouldSingleLine then begin - if (rpfIndent in FFormatFlags) then begin - sep := ',' + FLineSeparator + FIndentString; - sep2 := FLineSeparator + FIndentString; - end - else begin - sep := ',' + FLineSeparator; - sep2 := FLineSeparator; - end; - if Cnt > 1 then - FHasLineBreak := True; + if FCurrentArrayLenShown then + CouldHide := False; + FCurrentArrayLenShown := FCurrentArrayLenShown or LoopCurrentArrayLenShown; + + if FHasLineBreak or + ( ( (ANestLvl < FDeepestArray) or (MultiLine.ForceSingleLineReverseDepth <= 1) ) and + (AResValue.FieldCount > MultiLine.ForceSingleLineThresholdStructFld) ) or + (Min(FDeepestMultilineLvl, MultiLine.MaxMultiLineDepth) - FCurrentMultilineLvl >= MultiLine.ForceSingleLineReverseDepth) + then + CouldSingleLine := False; + + if CutOff then begin + Result.RawPartsAsString[Cnt] := '...'; + inc(Cnt); + end; + + if ShowMultiLine and not CouldSingleLine then begin + if (rpfIndent in FFormatFlags) then begin + sep := ',' + FLineSeparator + FIndentString; + sep2 := FLineSeparator + FIndentString; end else begin - sep := ', '; - sep2 := ''; + sep := ',' + FLineSeparator; + sep2 := FLineSeparator; end; - - if (Cnt > 1) and (not CouldSingleLine) then - Results[Cnt-1] := Results[Cnt-1] + sep2 +')' - else - Results[Cnt-1] := Results[Cnt-1] +')'; - - if CouldHide and - (ANestLvl - FCurrentOuterMostArrayLvl >= LenPrefix.HideLenKeepDepth) - then - ShowLen := False; - if ShowLen then begin - Results[0] := LenStr + sep2 + '(' + Results[0]; - FCurrentArrayLenShown := True; - end - else - Results[0] := '(' + Results[0]; - - Result := AnsiString.Join(sep, Results); + if Result.RawPartCount > 1 then + FHasLineBreak := True; end else begin - if ShowLen then - Result := LenStr + '()' - else - Result := '()'; + sep := ', '; + sep2 := ''; end; + Result.RawSeparator := sep; + + //if (Cnt > 1) and (not CouldSingleLine) then + if FHasLineBreak and (not CouldSingleLine) then + Result.RawPostfix := sep2 +')' + else + Result.RawPostfix := ')'; + + if CouldHide and + (ANestLvl - FCurrentOuterMostArrayLvl >= LenPrefix.HideLenKeepDepth) + then + ShowLen := False; + if ShowLen then begin + Result.RawPrefix := LenStr + sep2 + '('; + FCurrentArrayLenShown := True; + end + else + Result.RawPrefix := '('; if OldHasLineBreak then FHasLineBreak := True; @@ -927,7 +928,8 @@ begin end; function TWatchResultPrinter.PrintStruct(AResValue: TWatchResultData; - const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): String; + const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String + ): TStringBuilderPart; const VisibilityNames: array [TLzDbgFieldVisibility] of string = ( '', 'private', 'protected', 'public', 'published' @@ -939,48 +941,51 @@ var vis, sep, tn, Header, we, CurIndentString: String; InclVisSect, OldHasLineBreak, CouldSingleLine, ShowMultiLine: Boolean; MultiLine: TWatchDisplayFormatMultiline; - Results: array of string; FldIdx, Len, ForceSingleLineEach, ElemCnt, SepLen: Integer; + RR: TStringBuilderPart; begin inc(FCurrentMultilineLvl); Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue); - Result := ''; tn := AResValue.TypeName; + Header := ''; if (AResValue.StructType in [dstClass, dstInterface]) then begin if (AResValue.DataAddress = 0) then begin - //Result := PrintNumber(0, 0, FTargetAddressSize, Resolved.Num2); - Result := 'nil'; + //Header := PrintNumber(0, 0, FTargetAddressSize, Resolved.Num2); if (Resolved.Address.TypeFormat = vdfAddressTyped) and (tn <> '') then - Result := tn + '(' + Result + ')'; + Result.RawAsStringPtr^ := tn + '(nil)' + else + Result.RawAsString := 'nil'; exit; end; if (Resolved.Struct.ShowPointerFormat <> vdfStructPointerOff) or (AResValue.FieldCount = 0) then begin // TODO: for 32 bit target, sign extend the 2nd argument - Result := PrintNumber(AResValue.DataAddress, Int64(AResValue.DataAddress), FTargetAddressSize, Resolved.Num2, True); + Header := PrintNumber(AResValue.DataAddress, Int64(AResValue.DataAddress), FTargetAddressSize, Resolved.Num2, True); if (Resolved.Address.TypeFormat = vdfAddressTyped) and (tn <> '') then begin - Result := tn + '(' + Result + ')'; + Header := tn + '(' + Header + ')'; tn := ''; end; - if (Resolved.Struct.ShowPointerFormat = vdfStructPointerOnly) or (AResValue.FieldCount = 0) then + if (Resolved.Struct.ShowPointerFormat = vdfStructPointerOnly) or (AResValue.FieldCount = 0) then begin + Result.RawAsString := Header; exit; - end; - end; - Header := Result; - Result := ''; + end; - if Header <> '' then - Header := Header + ': '; - if (Resolved.Struct.DataFormat <> vdfStructFull) and - not(AResValue.StructType in [dstClass, dstInterface]) + if Header <> '' then + Header := Header + ': '; + end; + end + else + if (Resolved.Struct.DataFormat <> vdfStructFull) + //and not(AResValue.StructType in [dstClass, dstInterface]) then tn := ''; + if AResValue.FieldCount = 0 then begin - Result := Header + tn + '()'; + Result.RawAsStringPtr^ := Header + tn + '()'; exit; end; @@ -1016,16 +1021,12 @@ begin inc(FldIdx); if InclVisSect then inc(FldIdx); - SetLength(Results, AResValue.FieldCount * FldIdx+1); + Result.RawPartCount := AResValue.FieldCount * FldIdx; FldIdx := 0; - if (Header <> '') or (tn <> '') then begin - Results[FldIdx] := Header + tn + '('; - inc(FldIdx); - end; Len := 0; for FldInfo in AResValue do begin if Len > 1 + 1000*1000 div Max(1, ANestLvl*4) then begin - Results[FldIdx] := '...'; + Result.RawPartsAsString[FldIdx] := '...'; inc(FldIdx); break; end; @@ -1037,8 +1038,8 @@ begin if (Resolved.Struct.DataFormat = vdfStructFull) and (FldOwner <> nil) and (FldOwner.DirectFieldCount > 0) and (AResValue.StructType in [dstClass, dstInterface, dstObject]) // record has no inheritance then begin - Results[FldIdx] := '{' + FldOwner.TypeName + '}'; - Len := Len + Length(Results[FldIdx]) + SepLen; + Result.RawPartsAsString[FldIdx] := '{' + FldOwner.TypeName + '}'; + Len := Len + Result.PartsTotalLen[FldIdx] + SepLen; inc(FldIdx); inc(FElementCount); end; @@ -1046,21 +1047,31 @@ begin if InclVisSect and (vis <> VisibilityNames[FldInfo.FieldVisibility]) then begin vis := VisibilityNames[FldInfo.FieldVisibility]; - Results[FldIdx] := vis; - Len := Len + Length(Results[FldIdx]) + SepLen; + Result.RawPartsAsString[FldIdx] := vis; + Len := Len + Result.PartsTotalLen[FldIdx] + SepLen; inc(FldIdx); inc(FElementCount); end; ElemCnt := FElementCount; - Results[FldIdx] := PrintWatchValueEx(FldInfo.Field, ADispFormat, ANestLvl, we + UpperCase(FldInfo.FieldName)) + ';'; - if Resolved.Struct.DataFormat <> vdfStructValOnly then - Results[FldIdx] := FldInfo.FieldName + ': ' + Results[FldIdx]; - Len := Len + Length(Results[FldIdx]) + SepLen; + + if Resolved.Struct.DataFormat <> vdfStructValOnly then begin + RR.RawPartCount := 2; + RR.RawPartsAsString[0] := FldInfo.FieldName; + RR.RawSeparator := ': '; + RR.RawParts[1] := PrintWatchValueEx(FldInfo.Field, ADispFormat, ANestLvl, we + UpperCase(FldInfo.FieldName)); + end + else begin + RR.RawPartCount := 1; + RR.RawParts[0] := PrintWatchValueEx(FldInfo.Field, ADispFormat, ANestLvl, we + UpperCase(FldInfo.FieldName)); + end; + RR.RawPostfix := '; '; + Result.RawParts[FldIdx] := RR; + Len := Len + RR.TotalLen + SepLen; if CouldSingleLine and ( ( (MultiLine.ForceSingleLineThresholdLen > 0) and - (Length(Results[FldIdx]) > MultiLine.ForceSingleLineThresholdLen) + (Result.PartsTotalLen[FldIdx] > MultiLine.ForceSingleLineThresholdLen) ) or (FElementCount - ElemCnt > ForceSingleLineEach) or ( (MultiLine.ForceSingleLineThresholdEach = 0) and (FldInfo.Field.ValueKind in [rdkArray, rdkStruct]) ) @@ -1071,7 +1082,7 @@ begin inc(FldIdx); end; - SetLength(Results, FldIdx); + Result.RawChangePartCount(FldIdx); if FHasLineBreak or (Min(FDeepestMultilineLvl, MultiLine.MaxMultiLineDepth) - FCurrentMultilineLvl > MultiLine.ForceSingleLineReverseDepth) then @@ -1085,29 +1096,33 @@ begin end else sep := ' '; + Result.RawSeparator := sep; - - dec(FldIdx); if FHasLineBreak then - Results[FldIdx] := Results[FldIdx] + sep + ')' + Result.RawPostfix := sep + ')' else - Results[FldIdx] := Results[FldIdx] + ')'; + Result.RawPostfix := ')'; - if not ((Header <> '') or (tn <> '')) then - Results[0] := '(' + Results[0]; + if (Header <> '') or (tn <> '') then begin + Result.RawPrefix := Header + tn + '(' + Sep; + FHasLineBreak := sep <> ' '; + end + else + Result.RawPrefix := '('; - Result := AnsiString.Join(sep, Results); - - if ((FldIdx > 0) and (Sep <> ' ')) or OldHasLineBreak then + if ((Result.RawPartCount > 1) and (Sep <> ' ')) or OldHasLineBreak then FHasLineBreak := True; FIndentString := CurIndentString; end; function TWatchResultPrinter.PrintConverted(AResValue: TWatchResultData; - const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): String; + const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String + ): TStringBuilderPart; begin - if AResValue.FieldCount = 0 then - exit('Error: No result'); + if AResValue.FieldCount = 0 then begin + Result.RawAsString := 'Error: No result'; + exit; + end; if (AResValue.FieldCount = 1) or ( (AResValue.Fields[0].Field <> nil) and @@ -1119,31 +1134,35 @@ begin end; if (AResValue.FieldCount > 1) then begin - Result := PrintWatchValueEx(AResValue.Fields[1].Field, ADispFormat, ANestLvl, AWatchedExpr); if (AResValue.Fields[0].Field = nil) or (AResValue.Fields[0].Field.ValueKind <> rdkError) or (AResValue.Fields[0].Field.AsString <> '') - then - Result := Result + ' { ' - + PrintWatchValueEx(AResValue.Fields[0].Field, ADispFormat, ANestLvl, AWatchedExpr) - + ' }'; + then begin + Result.RawPartCount := 4; + Result.RawParts[0] := PrintWatchValueEx(AResValue.Fields[1].Field, ADispFormat, ANestLvl, AWatchedExpr); + Result.RawPartsAsString[1] := ' { '; + Result.RawParts[2] := PrintWatchValueEx(AResValue.Fields[0].Field, ADispFormat, ANestLvl, AWatchedExpr); + Result.RawPartsAsString[3] := ' }'; + end + else + Result := PrintWatchValueEx(AResValue.Fields[1].Field, ADispFormat, ANestLvl, AWatchedExpr); exit; end; - Result := 'Error: No result'; + Result.RawAsString := 'Error: No result'; end; function TWatchResultPrinter.PrintProc(AResValue: TWatchResultData; - const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer): String; + const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer): TStringBuilderPart; var Resolved: TResolvedDisplayFormat; - s: String; + s, R: String; begin Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue); - Result := PrintNumber(AResValue.AsQWord, AResValue.AsInt64, TargetAddressSize, Resolved.Num2, True); + R := PrintNumber(AResValue.AsQWord, AResValue.AsInt64, TargetAddressSize, Resolved.Num2, True); if AResValue.AsString <> '' then - Result := Result + ' = ' + AResValue.AsString; + R := R + ' = ' + AResValue.AsString; if ANestLvl > 0 then begin s := AResValue.TypeName; @@ -1156,13 +1175,15 @@ begin if s <> '' then if AResValue.ValueKind in [rdkFunctionRef, rdkProcedureRef] then - Result := Result + ': '+s + R := R + ': '+s else - Result := s + ' AT ' +Result; + R := s + ' AT ' +R; + Result.RawAsString := R; end; function TWatchResultPrinter.PrintWatchValueEx(AResValue: TWatchResultData; - const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String): String; + const ADispFormat: TWatchDisplayFormat; ANestLvl: Integer; const AWatchedExpr: String + ): TStringBuilderPart; function PrintChar: String; var @@ -1260,11 +1281,12 @@ function TWatchResultPrinter.PrintWatchValueEx(AResValue: TWatchResultData; var PointerValue: TWatchResultDataPointer absolute AResValue; - ResTypeName: String; + ResTypeName, R: String; PtrDeref, PtrDeref2, OldCurrentResValue, OldParentResValue: TWatchResultData; Resolved: TResolvedDisplayFormat; n, OldCurrentMultilineLvl: Integer; StoredSettings: TWatchResStoredSettings; + R2: PString; begin inc(ANestLvl); OldCurrentResValue := FCurrentResValue; @@ -1274,10 +1296,14 @@ begin FCurrentResValue := AResValue; inc(FElementCount); try - if ANestLvl > MAX_ALLOWED_NEST_LVL then - exit('...'); - if AResValue = nil then - exit('???'); + if ANestLvl > MAX_ALLOWED_NEST_LVL then begin + Result.RawAsString := '...'; + exit; + end; + if AResValue = nil then begin + Result.RawAsString := '???'; + exit; + end; if FCurrentValueFormatter <> nil then begin StoreSetting(StoredSettings); @@ -1288,8 +1314,10 @@ begin FWatchedExprInFormatter := AWatchedExpr; // try - if FCurrentValueFormatter.FormatValue(AResValue, ADispFormat, ANestLvl, Self, Result, FWatchedVarName, AWatchedExpr) then + R2 := Result.RawAsStringPtr; + if FCurrentValueFormatter.FormatValue(AResValue, ADispFormat, ANestLvl, Self, R2^, FWatchedVarName, AWatchedExpr) then begin exit; + end; finally FNextCallIsValueFormatter := False; RestoreSetting(StoredSettings); @@ -1298,33 +1326,36 @@ begin else FCurrentValueFormatter := FNextValueFormatter; - Result := ''; + Result.Init; case AResValue.ValueKind of rdkError: begin - Result := 'Error: ' + AResValue.AsString; if rpfClearMultiLine in FFormatFlags then - Result := ClearMultiline(Result); + Result.RawAsStringPtr^ := 'Error: ' + ClearMultiline(AResValue.AsString) + else + Result.RawAsStringPtr^ := 'Error: ' + AResValue.AsString; end; rdkUnknown: - Result := 'Error: Unknown'; + Result.RawAsStringPtr^ := 'Error: Unknown'; rdkPrePrinted: begin - Result := AResValue.AsString; if rpfClearMultiLine in FFormatFlags then - Result := ClearMultiline(Result); + Result.RawAsStringPtr^ := AResValue.AsString + else + Result.RawAsStringPtr^ := ClearMultiline(AResValue.AsString); end; rdkSignedNumVal, rdkUnsignedNumVal: begin Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue); - Result := PrintNumber(AResValue.AsQWord, AResValue.AsInt64, AResValue.ByteSize, Resolved.Num1); + R2 := Result.RawAsStringPtr; + R2^ := PrintNumber(AResValue.AsQWord, AResValue.AsInt64, AResValue.ByteSize, Resolved.Num1); if Resolved.Num2.Visible then begin - Result := Result +' = ' + + R2^ := R2^ +' = ' + PrintNumber(AResValue.AsQWord, AResValue.AsInt64, AResValue.ByteSize, Resolved.Num2); end; end; rdkPointerVal: begin Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue); - Result := ''; + R := ''; PtrDeref := PointerValue.DerefData; if (Resolved.Pointer.DerefFormat = vdfPointerDerefOnly) then @@ -1332,7 +1363,7 @@ begin if (Resolved.Pointer.DerefFormat <> vdfPointerDerefOnly) or (PtrDeref = nil) then begin n := AResValue.ByteSize; if n = 0 then n := FTargetAddressSize; - Result := PrintNumber(AResValue.AsQWord, AResValue.AsInt64, n, Resolved.Num2, True); + R := PrintNumber(AResValue.AsQWord, AResValue.AsInt64, n, Resolved.Num2, True); if Resolved.Pointer.Address.TypeFormat = vdfAddressTyped then begin ResTypeName := AResValue.TypeName; if (ResTypeName = '') and (PtrDeref <> nil) then begin @@ -1341,50 +1372,63 @@ begin ResTypeName := '^'+ResTypeName; end; if ResTypeName <> '' then - Result := ResTypeName + '(' + Result + ')'; + R := ResTypeName + '(' + R + ')'; end; end; if (Resolved.Pointer.DerefFormat <> vdfPointerDerefOff) and (PtrDeref <> nil) then begin while (PtrDeref.ValueKind = rdkPointerVal) and (PtrDeref.DerefData <> nil) do begin PtrDeref2 := PtrDeref; - Result := Result + '^'; + R := R + '^'; PtrDeref := PtrDeref.DerefData; end; - if PtrDeref <> nil then - Result := Result + '^: ' + PrintWatchValueEx(PtrDeref, ADispFormat, ANestLvl, AWatchedExpr+'^') - else - Result := Result + ': ' + PrintWatchValueEx(PtrDeref2, ADispFormat, ANestLvl, AWatchedExpr+'^'); - end; + Result.RawPartCount := 2; + if PtrDeref <> nil then begin + R := R + '^: '; + Result.RawParts[1] := PrintWatchValueEx(PtrDeref, ADispFormat, ANestLvl, AWatchedExpr+'^'); + end + else begin + R := R + ': '; + Result.RawParts[1] := PrintWatchValueEx(PtrDeref2, ADispFormat, ANestLvl, AWatchedExpr+'^'); + end; + Result.RawPartsAsString[0] := R; + end + else + Result.RawAsString := R; end; rdkFloatVal: begin Resolved := DisplayFormatResolver.ResolveDispFormat(ADispFormat, AResValue); if Resolved.Float.NumFormat = vdfFloatScientific then case AResValue.FloatPrecission of - dfpSingle: Result := FloatToStrF(AResValue.AsFloat, ffExponent, 9, 0); - dfpDouble: Result := FloatToStrF(AResValue.AsFloat, ffExponent, 17, 0); - dfpExtended: Result := FloatToStrF(AResValue.AsFloat, ffExponent, 21, 0); + dfpSingle: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffExponent, 9, 0); + dfpDouble: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffExponent, 17, 0); + dfpExtended: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffExponent, 21, 0); end else case AResValue.FloatPrecission of - dfpSingle: Result := FloatToStrF(AResValue.AsFloat, ffGeneral, 9, 0); - dfpDouble: Result := FloatToStrF(AResValue.AsFloat, ffGeneral, 17, 0); - dfpExtended: Result := FloatToStrF(AResValue.AsFloat, ffGeneral, 21, 0); + dfpSingle: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffGeneral, 9, 0); + dfpDouble: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffGeneral, 17, 0); + dfpExtended: Result.RawAsStringPtr^ := FloatToStrF(AResValue.AsFloat, ffGeneral, 21, 0); end; end; - rdkChar: Result := PrintChar; - rdkString: Result := QuoteText(AResValue.AsString); - rdkWideString: Result := QuoteWideText(AResValue.AsWideString); - rdkBool: Result := PrintBool; + rdkChar: Result.RawAsStringPtr^ := PrintChar; + rdkString: Result.RawAsStringPtr^ := QuoteText(AResValue.AsString); + rdkWideString: Result.RawAsStringPtr^ := QuoteWideText(AResValue.AsWideString); + rdkBool: Result.RawAsStringPtr^ := PrintBool; rdkEnum, rdkEnumVal: - Result := PrintEnum; - rdkSet: Result := PrintSet; + Result.RawAsStringPtr^ := PrintEnum; + rdkSet: Result.RawAsStringPtr^ := PrintSet; rdkPCharOrString: begin + Result.RawPartCount := 4; AResValue.SetSelectedIndex(0); // pchar res - Result := 'PChar: ' + PrintWatchValueEx(AResValue.SelectedEntry, ADispFormat, ANestLvl, AWatchedExpr); + Result.RawPartsAsString[0] := 'PChar: '; + Result.RawParts[1] := PrintWatchValueEx(AResValue.SelectedEntry, ADispFormat, ANestLvl, AWatchedExpr); AResValue.SetSelectedIndex(1); // string res - Result := Result + FLineSeparator - + 'String: ' + PrintWatchValueEx(AResValue.SelectedEntry, ADispFormat, ANestLvl, AWatchedExpr); + if rpfClearMultiLine in FFormatFlags then + Result.RawPartsAsString[2] := ' - String: ' + else + Result.RawPartsAsString[2] := FLineSeparator + 'String: '; + Result.RawParts[3] := PrintWatchValueEx(AResValue.SelectedEntry, ADispFormat, ANestLvl, AWatchedExpr); end; rdkArray: Result := PrintArray(AResValue, ADispFormat, ANestLvl, AWatchedExpr); rdkStruct: Result := PrintStruct(AResValue, ADispFormat, ANestLvl, AWatchedExpr); @@ -1419,6 +1463,8 @@ end; function TWatchResultPrinter.PrintWatchValue(AResValue: TWatchResultData; const ADispFormat: TWatchDisplayFormat; const AWatchedExpr: String): String; +var + Res: TStringBuilderPart; begin FNextValueFormatter := nil; if FOnlyValueFormatter <> nil then @@ -1443,7 +1489,9 @@ begin FCurrentMultilineLvl := 0; FDeepestMultilineLvl := 0; FDeepestArray := 0; - Result := PrintWatchValueEx(AResValue, ADispFormat, -1, FWatchedVarName); + Res := PrintWatchValueEx(AResValue, ADispFormat, -1, FWatchedVarName); + Result := Res.GetFullString; + Res.FreeAll; end; function TWatchResultPrinter.PrintWatchValueIntf(AResValue: IWatchResultDataIntf; @@ -1451,6 +1499,7 @@ function TWatchResultPrinter.PrintWatchValueIntf(AResValue: IWatchResultDataIntf var AResValObj: TWatchResultData; IncLvl: Integer; + Res: TStringBuilderPart; begin AResValObj := TWatchResultData(AResValue.GetInternalObject); FNextValueFormatter := nil; @@ -1470,7 +1519,9 @@ begin if FNextCallIsValueFormatter then begin FNextCallIsValueFormatter := False; - Result := PrintWatchValueEx(AResValObj, ADispFormat, FInValFormNestLevel - 1 + IncLvl, FWatchedExprInFormatter); // This will increase it by one, compared to the value given to the formatter + Res := PrintWatchValueEx(AResValObj, ADispFormat, FInValFormNestLevel - 1 + IncLvl, FWatchedExprInFormatter); // This will increase it by one, compared to the value given to the formatter + Result := Res.GetFullString; + Res.FreeAll; end else begin // TOOD: full init? Or Call PrintWatchValueEx ? @@ -1481,7 +1532,9 @@ begin FCurrentMultilineLvl := 0; FDeepestMultilineLvl := 0; FDeepestArray := 0; - Result := PrintWatchValueEx(AResValObj, ADispFormat, -1, FWatchedExprInFormatter); + Res := PrintWatchValueEx(AResValObj, ADispFormat, -1, FWatchedExprInFormatter); + Result := Res.GetFullString; + Res.FreeAll; end; end; diff --git a/ide/packages/idedebugger/idedebuggerwatchresutils.pas b/ide/packages/idedebugger/idedebuggerwatchresutils.pas index 2e2a77c895..add337111e 100644 --- a/ide/packages/idedebugger/idedebuggerwatchresutils.pas +++ b/ide/packages/idedebugger/idedebuggerwatchresutils.pas @@ -1,6 +1,8 @@ unit IdeDebuggerWatchResUtils; {$mode objfpc}{$H+} +{$IFDEF INLINE_OFF}{ $INLINE OFF}{$ENDIF} +{$ModeSwitch advancedrecords} interface @@ -8,6 +10,140 @@ uses Classes, SysUtils, IdeDebuggerWatchResult, LazDebuggerIntf, IdeDebuggerWatchValueIntf; +type + + PStringBuilderPart = ^TStringBuilderPart; + + { TStringBuilderPart } + + TStringBuilderPart = record// object + private type + PHeader = ^THeader; + THeader = record + FTotalLen, FCount: Integer; + FPrefix, FPostfix, FSeparator: String; + end; + private const + HD_SIZE = sizeof(THeader); + private + FData: Pointer; + FType: (sbfString, sbfStringList, sbfPartList); + function GetAsString: String; inline; + function GetStringCount: Integer; inline; + function GetStrings(AnIndex: Integer): String; inline; + function GetPartCount: Integer; inline; + function GetParts(AnIndex: Integer): TStringBuilderPart; inline; + function GetPartsAsString(AnIndex: Integer): String; inline; + function GetPartsTotalLen(AnIndex: Integer): Integer; inline; + function GetPostfix: String; inline; + function GetPrefix: String; inline; + function GetSeparator: String; inline; + function GetRawPartsPtr(AnIndex: Integer): PStringBuilderPart; inline; + function GetTotalLen: integer; inline; + procedure SetAsString(const AValue: String); inline; + procedure SetStringCount(AValue: Integer); inline; + procedure SetStrings(AnIndex: Integer; const AValue: String); inline; + procedure SetPartCount(AValue: Integer); inline; + procedure SetParts(AnIndex: Integer; AValue: TStringBuilderPart); inline; + procedure SetPartsAsString(AnIndex: Integer; const AValue: String); inline; + procedure SetPostfix(const AValue: String); inline; + procedure SetPrefix(const AValue: String); inline; + procedure SetSeparator(const AValue: String); inline; + procedure SetRawAsString(const AValue: String); inline; + procedure SetRawStringCount(AValue: Integer); inline; + procedure SetRawStrings(AnIndex: Integer; const AValue: String); inline; + procedure SetRawPartCount(AValue: Integer); inline; + procedure SetRawParts(AnIndex: Integer; const AValue: TStringBuilderPart); inline; + procedure SetRawPartsAsString(AnIndex: Integer; const AValue: String); + procedure SetRawPostfix(const AValue: String); inline; + procedure SetRawPrefix(const AValue: String); inline; + procedure SetRawSeparator(const AValue: String); inline; + procedure DoFreeAll; + procedure WriteTo(var ADest: PChar); + public + procedure Init; inline; // Similar to "RawAsString := ''" + procedure FreeAll; inline; + function GetFullString: String; + property TotalLen: integer read GetTotalLen; + + public + (* * Init: New TStringBuilderPart must be initialized with "Init" + * Strings[], Parts[], PartsAsString: + - Must only be accessed when the builder has been set to the matching + list type using StringCount or PartCount (index must be in range / no checks) + * Parts[]: + - Must not be changed inline. + - They must be assigned a new TStringBuilderPart that has its final value + - Once assigned changes can be made using PartAsString, AppendToPart, PrependToPart + * Prefix, PostFix, Separator: + - Must only be accessed when the builder is a list + *) + + (* *** Single String sbfString *** *) + property AsString: String read GetAsString write SetAsString; + + (* *** List of String sbfStringList *** *) + property StringCount: Integer read GetStringCount write SetStringCount; + procedure ChangeStringCount(ANewCount: Integer; ATrim: Boolean = False); + property Strings[AnIndex: Integer]: String read GetStrings write SetStrings; + + (* *** List of embedded Parts (sub-builders) sbfPartList *** *) + property PartCount: Integer read GetPartCount write SetPartCount; + procedure ChangePartCount(ANewCount: Integer; ATrim: Boolean = False); + property Parts[AnIndex: Integer]: TStringBuilderPart read GetParts write SetParts; + // Set the "AsString" of a part that is already in the list + property PartsAsString[AnIndex: Integer]: String read GetPartsAsString write SetPartsAsString; + property PartsTotalLen[AnIndex: Integer]: Integer read GetPartsTotalLen; + + // Append/Prepend to a part (either to the string, or Pre/FPostFix + procedure PrependToPart(AnIndex: Integer; const AValue: String); + procedure AppendToPart(AnIndex: Integer; const AValue: String); + + (* *** List ... *** *) + property Prefix: String read GetPrefix write SetPrefix; + property Postfix: String read GetPostfix write SetPostfix; + property Separator: String read GetSeparator write SetSeparator; + + public + (* *** RAW ACCESS *** + * All data must be user initialized + - The following methods will initialize data. (They are "write once". They must not be used to modify data): + Init, RawAsString, RawPartCount, RawStringCount, RawPartsAsString + * Once data/type (string, string-list, part-list) has been set/chosen, it must not be changed anymore + * RawParts: + - Each Part in "RawParts" must be either + - assigned once (with a part that has its data already) + - set with "PartAsString" + - Once a sub-part has been added, it must not be replaced + - Once a sub-part has been added, it must not be modified anymore + (except with Prepend/AppendToPart) + - Once a sub-part has been added, it is owned and must not be freed by itself + - Once a value has been set (subvalue, post/Rawprefix, ...), it must not be changed + - Change...Count is only to cut off uninitialized entries. (data will not be freed, if it had been assigned) + - Before "FreeAll": All strings/Rawparts must have been initialized (use RawChangeCount to cut off uninitialized entries) + *** Ptr / Pointer to String *** + * Using the builders string variable (via pointer) allows the compiler to reduce temporary string vars + *) + (* *** Single String sbfString *** *) + property RawAsString: String read GetAsString write SetRawAsString; + function RawAsStringPtr: PString; + (* *** List of String sbfStringList *** *) + property RawStringCount: Integer read GetStringCount write SetRawStringCount; + procedure RawChangeStringCount(ANewCount: Integer); + property RawStrings[AnIndex: Integer]: String read GetStrings write SetRawStrings; + (* *** List of embedded RawParts (sub-builders) sbfPartList *** *) + property RawPartCount: Integer read GetPartCount write SetRawPartCount; + procedure RawChangePartCount(ANewCount: Integer); + property RawParts[AnIndex: Integer]: TStringBuilderPart read GetParts write SetRawParts; + property RawPartsPtr[AnIndex: Integer]: PStringBuilderPart read GetRawPartsPtr; + // Set the "RawAsString" of a part that is already in the list (if it has no value yet) + property RawPartsAsString[AnIndex: Integer]: String read GetPartsAsString write SetRawPartsAsString; + (* *** List ... *** *) + property RawPrefix: String read GetPrefix write SetRawPrefix; + property RawPostfix: String read GetPostfix write SetRawPostfix; + property RawSeparator: String read GetSeparator write SetRawSeparator; + end; + function ExtractProcResFromMethod(AMethodRes: TWatchResultData): TWatchResultData; function ExtractInstanceResFromMethod(AMethodRes: TWatchResultData): TWatchResultData; @@ -47,5 +183,503 @@ begin Result := AMethodRes.Fields[1].Field; end; +{ TStringBuilderPart } + +function TStringBuilderPart.GetAsString: String; +begin + if FType = sbfString then + Result := String(FData) + else + Result := ''; +end; + +function TStringBuilderPart.GetStringCount: Integer; +begin + if (FType = sbfStringList) and (FData <> nil) then + Result := PHeader(FData)^.FCount + else + Result := 0; +end; + +function TStringBuilderPart.GetStrings(AnIndex: Integer): String; +begin + Result := PString(FData + HD_SIZE)[AnIndex]; +end; + +function TStringBuilderPart.GetPartCount: Integer; +begin + if (FType = sbfPartList) and (FData <> nil) then + Result := PHeader(FData)^.FCount + else + Result := 0; +end; + +function TStringBuilderPart.GetParts(AnIndex: Integer): TStringBuilderPart; +begin + Result := PStringBuilderPart(FData + HD_SIZE)[AnIndex]; +end; + +function TStringBuilderPart.GetPartsAsString(AnIndex: Integer): String; +var + p: PStringBuilderPart; +begin + p := @PStringBuilderPart(FData + HD_SIZE)[AnIndex]; + if p^.FType = sbfString then + Result := String(p^.FData) + else + Result := ''; +end; + +function TStringBuilderPart.GetPartsTotalLen(AnIndex: Integer): Integer; +var + p: PStringBuilderPart; +begin + p := @PStringBuilderPart(FData + HD_SIZE)[AnIndex]; + if p^.FType = sbfString then + Result := Length(String(p^.FData)) + else + Result := PHeader(p^.FData)^.FTotalLen; +end; + +function TStringBuilderPart.GetPostfix: String; +begin + if (FType <> sbfString) and (FData <> nil) then + Result := PHeader(FData)^.FPostfix + else + Result := ''; +end; + +function TStringBuilderPart.GetPrefix: String; +begin + if (FType <> sbfString) and (FData <> nil) then + Result := PHeader(FData)^.FPostfix + else + Result := ''; +end; + +function TStringBuilderPart.GetSeparator: String; +begin + if (FType <> sbfString) and (FData <> nil) then + Result := PHeader(FData)^.FSeparator + else + Result := ''; +end; + +function TStringBuilderPart.GetRawPartsPtr(AnIndex: Integer): PStringBuilderPart; +begin + Result := @PStringBuilderPart(FData + HD_SIZE)[AnIndex]; +end; + +function TStringBuilderPart.GetTotalLen: integer; +begin + if (FType <> sbfString) and (FData <> nil) then + Result := PHeader(FData)^.FTotalLen + else + Result := Length(string(FData)); +end; + +procedure TStringBuilderPart.SetAsString(const AValue: String); +begin + if FType <> sbfString then FreeAll; + FType := sbfString; + String(FData) := AValue; +end; + +procedure TStringBuilderPart.SetStringCount(AValue: Integer); +begin + if FType = sbfStringList then begin + ChangeStringCount(AValue, PHeader(FData)^.FCount > 2 * AValue); + end + else begin + if FData <> nil then + FreeAll; + FType := sbfStringList; + FData := AllocMem(HD_SIZE + AValue * SizeOf(string)); + PHeader(FData)^.FCount := AValue; + end; +end; + +procedure TStringBuilderPart.SetStrings(AnIndex: Integer; const AValue: String); +var + ps: PString; +begin + ps := @PString(FData + HD_SIZE)[AnIndex]; + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen - Length(ps^) + Length(AValue); + ps^ := AValue; +end; + +procedure TStringBuilderPart.SetPartCount(AValue: Integer); +begin + if FType = sbfPartList then begin + ChangePartCount(AValue, PHeader(FData)^.FCount > 2 * AValue); + end + else begin + if FData <> nil then + FreeAll; + FType := sbfPartList; + FData := AllocMem(HD_SIZE + AValue * SizeOf(TStringBuilderPart)); + PHeader(FData)^.FCount := AValue; + end; +end; + +procedure TStringBuilderPart.SetParts(AnIndex: Integer; AValue: TStringBuilderPart); +var + pp: PStringBuilderPart; +begin + pp := @PStringBuilderPart(FData + HD_SIZE)[AnIndex]; + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen - pp^.TotalLen + AValue.TotalLen; + pp^ := AValue; +end; + +procedure TStringBuilderPart.SetPartsAsString(AnIndex: Integer; const AValue: String); +var + p: PStringBuilderPart; + t: Integer; +begin + p := @PStringBuilderPart(FData + HD_SIZE)[AnIndex]; + t := PHeader(FData)^.FTotalLen; + if p^.FType <> sbfString then begin + t := t - + p^.TotalLen; + p^.FreeAll; + end + else + t := t - Length(String(p^.FData)); + + p^.FType := sbfString; + String(p^.FData) := AValue; + t := t + Length(AValue); + PHeader(FData)^.FTotalLen := t; +end; + +procedure TStringBuilderPart.SetPostfix(const AValue: String); +begin + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen - Length(PHeader(FData)^.FPostfix) + Length(AValue); + PHeader(FData)^.FPostfix := AValue; +end; + +procedure TStringBuilderPart.SetPrefix(const AValue: String); +begin + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen - Length(PHeader(FData)^.FPrefix) + Length(AValue); + PHeader(FData)^.FPrefix := AValue; +end; + +procedure TStringBuilderPart.SetSeparator(const AValue: String); +begin + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + (Length(AValue) - Length(PHeader(FData)^.FSeparator)) * (PHeader(FData)^.FCount - 1); + PHeader(FData)^.FSeparator := AValue; +end; + +procedure TStringBuilderPart.SetRawAsString(const AValue: String); +begin + FType := sbfString; + FData := nil; + String(FData) := AValue; +end; + +procedure TStringBuilderPart.SetRawStringCount(AValue: Integer); +begin + FType := sbfStringList; + FData := GetMem(HD_SIZE + AValue * SizeOf(string)); + FillChar(FData^,HD_SIZE,0); + PHeader(FData)^.FCount := AValue; +end; + +procedure TStringBuilderPart.SetRawStrings(AnIndex: Integer; const AValue: String); +begin + PString(FData + HD_SIZE)[AnIndex] := AValue; + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + Length(AValue); +end; + +procedure TStringBuilderPart.SetRawPartCount(AValue: Integer); +begin + FType := sbfPartList; + FData := GetMem(HD_SIZE + AValue * SizeOf(TStringBuilderPart)); + FillChar(FData^,HD_SIZE,0); + PHeader(FData)^.FCount := AValue; +end; + +procedure TStringBuilderPart.SetRawParts(AnIndex: Integer; const AValue: TStringBuilderPart); +begin + PStringBuilderPart(FData + HD_SIZE)[AnIndex] := AValue; + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + AValue.TotalLen; +end; + +procedure TStringBuilderPart.SetRawPartsAsString(AnIndex: Integer; const AValue: String); +var + p: PStringBuilderPart; +begin + p := @PStringBuilderPart(FData + HD_SIZE)[AnIndex]; + p^.FType := sbfString; + p^.FData := nil; + String(p^.FData) := AValue; + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + Length(AValue); +end; + +procedure TStringBuilderPart.SetRawPostfix(const AValue: String); +begin + PHeader(FData)^.FPostfix := AValue; + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + Length(AValue); +end; + +procedure TStringBuilderPart.SetRawPrefix(const AValue: String); +begin + PHeader(FData)^.FPrefix := AValue; + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + Length(AValue); +end; + +procedure TStringBuilderPart.SetRawSeparator(const AValue: String); +begin + PHeader(FData)^.FSeparator := AValue; + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + Length(AValue) * (PHeader(FData)^.FCount - 1); +end; + +procedure TStringBuilderPart.DoFreeAll; +var + ps: PString; + pp: PStringBuilderPart; + i: Integer; +begin + case FType of + //sbfString: + // Finalize(String(FData)); + sbfStringList: begin + Finalize(PHeader(FData)^.FPrefix); + Finalize(PHeader(FData)^.FPostfix); + Finalize(PHeader(FData)^.FSeparator); + ps := PString(FData + HD_SIZE); + for i := 0 to PHeader(FData)^.FCount - 1 do begin + Finalize(ps^); + inc(ps); + end; + Freemem(FData); + end; + sbfPartList: begin + Finalize(PHeader(FData)^.FPrefix); + Finalize(PHeader(FData)^.FPostfix); + Finalize(PHeader(FData)^.FSeparator); + pp := PStringBuilderPart(FData + HD_SIZE); + for i := 0 to PHeader(FData)^.FCount - 1 do begin + if pp^.FType = sbfString then + Finalize(String(pp^.FData)) + else + //if pp^.FData <> nil then + pp^.DoFreeAll; + inc(pp); + end; + Freemem(FData); + end; + end; +end; + +procedure TStringBuilderPart.WriteTo(var ADest: PChar); +var + ps: PString; + pp: PStringBuilderPart; + i, c: Integer; + txt: PChar; + len: Integer; +begin + if FData = nil then + exit; + + case FType of + sbfString: begin + len := Length(String(FData)); + move(FData^, ADest^, len); + inc(ADest, len); + end; + sbfStringList: begin + len := Length(PHeader(FData)^.FPrefix); + if len > 0 then begin + move(PChar(PHeader(FData)^.FPrefix)^, ADest^, len); + inc(ADest, len) + end; + len := Length(PHeader(FData)^.FSeparator); + ps := PString(FData + HD_SIZE); + c := PHeader(FData)^.FCount; + if (c > 1) and (len > 0) then begin + txt := PChar(PHeader(FData)^.FSeparator); + for i := c - 2 downto 0 do begin + move(pchar(ps^)^, ADest^, Length(String(ps^))); + inc(ADest, Length(ps^)); + inc(ps); + move(txt^, ADest^, len); + inc(ADest, len) + end; + move(pchar(ps^)^, ADest^, Length(String(ps^))); + inc(ADest, Length(ps^)); + end + else begin + for i := c - 1 downto 0 do begin + move(pchar(ps^)^, ADest^, Length(String(ps^))); + inc(ADest, Length(ps^)); + inc(ps); + end; + end; + len := Length(PHeader(FData)^.FPostfix); + if len > 0 then begin + move(PChar(PHeader(FData)^.FPostfix)^, ADest^, len); + inc(ADest, len) + end; + end; + sbfPartList: begin + len := Length(PHeader(FData)^.FPrefix); + if len > 0 then begin + move(PChar(PHeader(FData)^.FPrefix)^, ADest^, len); + inc(ADest, len) + end; + len := Length(PHeader(FData)^.FSeparator); + pp := PStringBuilderPart(FData + HD_SIZE); + c := PHeader(FData)^.FCount; + if (c > 1) and (len > 0) then begin + txt := PChar(PHeader(FData)^.FSeparator); + for i := c - 2 downto 0 do begin + pp^.WriteTo(ADest); + inc(pp); + move(txt^, ADest^, len); + inc(ADest, len) + end; + pp^.WriteTo(ADest); + end + else begin + for i := c- 1 downto 0 do begin + pp^.WriteTo(ADest); + inc(pp); + end; + end; + len := Length(PHeader(FData)^.FPostfix); + if len > 0 then begin + move(PChar(PHeader(FData)^.FPostfix)^, ADest^, len); + inc(ADest, len) + end; + end; + end; +end; + +procedure TStringBuilderPart.Init; +begin + FData := Nil; +end; + +procedure TStringBuilderPart.FreeAll; +begin + if FData = nil then + exit; + + if FType = sbfString then + Finalize(String(FData)) + else + DoFreeAll; + FData := nil; +end; + +function TStringBuilderPart.GetFullString: String; +var + p: PChar; +begin + if FData = nil then + exit(''); + if FType = sbfString then + exit(string(FData)); + + SetLength(Result, PHeader(FData)^.FTotalLen); + p := pchar(Result); + WriteTo(p); + assert(p = Pchar(Result)+Length(Result), 'TStringBuilderPart.GetFullString: p = Pchar(Result)+Length(Result)'); +end; + +procedure TStringBuilderPart.ChangeStringCount(ANewCount: Integer; ATrim: Boolean); +var + i, t: Integer; + ps: PString; +begin + t := PHeader(FData)^.FTotalLen; + ps := @PString(FData + HD_SIZE)[ANewCount]; + for i := ANewCount to PHeader(FData)^.FCount -1 do begin + t := t - Length(ps^); + ps^ := ''; + inc(ps); + end; + if ATrim or (ANewCount > PHeader(FData)^.FCount) then + FData := ReAllocMem(FData, HD_SIZE + ANewCount * SizeOf(string)); + t := t + Length(PHeader(FData)^.FSeparator) * (ANewCount - PHeader(FData)^.FCount); + PHeader(FData)^.FTotalLen := t; + PHeader(FData)^.FCount := ANewCount; +end; + +procedure TStringBuilderPart.ChangePartCount(ANewCount: Integer; ATrim: Boolean); +var + i, t: Integer; + pp: PStringBuilderPart; +begin + t := PHeader(FData)^.FTotalLen; + pp := @PStringBuilderPart(FData + HD_SIZE)[ANewCount]; + for i := ANewCount to PHeader(FData)^.FCount -1 do begin + t := t - pp^.TotalLen; + pp^.FreeAll; + inc(pp); + end; + if ATrim or (ANewCount > PHeader(FData)^.FCount) then + FData := ReAllocMem(FData, HD_SIZE + ANewCount * SizeOf(TStringBuilderPart)); + t := t + Length(PHeader(FData)^.FSeparator) * (ANewCount - PHeader(FData)^.FCount); + PHeader(FData)^.FTotalLen := t; + PHeader(FData)^.FCount := ANewCount; +end; + +procedure TStringBuilderPart.PrependToPart(AnIndex: Integer; const AValue: String); +var + p: PStringBuilderPart; +begin + p := @PStringBuilderPart(FData + HD_SIZE)[AnIndex]; + case p^.FType of + sbfString: String(p^.FData) := AValue + String(p^.FData); + sbfStringList, + sbfPartList: begin + PHeader(p^.FData)^.FPrefix := AValue + PHeader(p^.FData)^.FPrefix; + PHeader(p^.FData)^.FTotalLen := PHeader(p^.FData)^.FTotalLen + Length(AValue); + end; + end; + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + Length(AValue); +end; + +procedure TStringBuilderPart.AppendToPart(AnIndex: Integer; const AValue: String); +var + p: PStringBuilderPart; +begin + p := @PStringBuilderPart(FData + HD_SIZE)[AnIndex]; + case p^.FType of + sbfString: String(p^.FData) := String(p^.FData) + AValue; + sbfStringList, + sbfPartList: begin + PHeader(p^.FData)^.FPostfix := PHeader(p^.FData)^.FPostfix + AValue; + PHeader(p^.FData)^.FTotalLen := PHeader(p^.FData)^.FTotalLen + Length(AValue); + end; + end; + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + Length(AValue); +end; + +function TStringBuilderPart.RawAsStringPtr: PString; +begin + FType := sbfString; + FData := nil; +Result := PString(@FData ) +end; + +procedure TStringBuilderPart.RawChangeStringCount(ANewCount: Integer); +begin + if ANewCount > PHeader(FData)^.FCount then + FData := ReAllocMem(FData, HD_SIZE + ANewCount * SizeOf(string)); + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + Length(PHeader(FData)^.FSeparator) * (ANewCount - PHeader(FData)^.FCount); + PHeader(FData)^.FCount := ANewCount; +end; + +procedure TStringBuilderPart.RawChangePartCount(ANewCount: Integer); +begin + if ANewCount > PHeader(FData)^.FCount then + FData := ReAllocMem(FData, HD_SIZE + ANewCount * SizeOf(TStringBuilderPart)); + PHeader(FData)^.FTotalLen := PHeader(FData)^.FTotalLen + Length(PHeader(FData)^.FSeparator) * (ANewCount - PHeader(FData)^.FCount); + PHeader(FData)^.FCount := ANewCount; +end; + end.