其实Managed Record有很多方便使用的场景。
比如下面的就是把TStringBuilder由Class改成了Managed Record。好处就是不再需要释放了。
{ Managed Record版本的StringBuilder。 Delphi在10.4开始终于支持Managed Record了。 加了这个有很多可以玩的了。 比如说有些原先是class实现的东西,可以用Managed Record来实现。 这样的好处是很多东西想用就用,用了不需要管什么时候释放。 因为Record是在栈里面的,系统会自动回收,回收前还会调用Finalize操作符。 不仅仅StringBuilder,其实很多都可以变成Managed Record。 例如用Managed Record,实现智能锁,离开代码块,锁就会自动释放了。 2020.9.9 武稀松 wr960204 } unit ManagedStringBuilder; interface uses System.Classes, System.SysUtils, System.RTLConsts, System.SysConst; type TCharArray = TArray<Char>; type TCharSearch = record ArrayPtr: PChar; MatchPtr: PChar; end; TStringBuilder = record private type TEnumerator = record private FPtr: PChar; FEndPtr: PChar; public procedure Initialize(const ABuilder: TStringBuilder); inline; function GetCurrent: Char; inline; function MoveNext: Boolean; inline; property Current: Char read GetCurrent; end; private const DefaultCapacity = $10; LineBreak: string = sLineBreak; private function GetCapacity: Integer; inline; procedure SetCapacity(Value: Integer); function GetChars(Index: Integer): Char; procedure SetChars(Index: Integer; Value: Char); function GetLength: Integer; inline; procedure SetLength(Value: Integer); function GetMaxCapacity: Integer; inline; procedure ExpandCapacity; procedure ReduceCapacity; procedure CheckBounds(Index: Integer); function _Replace(Index: Integer; const Old, New: string): Boolean; private FData: string; FLength: Integer; FMaxCapacity: Integer; public //constructor Create; class operator Initialize(out Dest: TStringBuilder); class operator Finalize(var Dest: TStringBuilder); //:=不需要处理 //class operator Assign(var Dest: TStringBuilder; const [ref] Src: TStringBuilder); constructor Create(aCapacity: Integer); overload; constructor Create(const Value: string); overload; constructor Create(aCapacity: Integer; aMaxCapacity: Integer); overload; constructor Create(const Value: string; aCapacity: Integer); overload; constructor Create(const Value: string; StartIndex: Integer; Length: Integer; aCapacity: Integer); overload; //占位,兼容class版本的TStringBuilder,这里是为了移植不需要改代码。空函数. procedure Free(); inline; function Append(const Value: Boolean): TStringBuilder; overload; function Append(const Value: Byte): TStringBuilder; overload; function Append(const Value: Char): TStringBuilder; overload; function Append(const Value: Currency): TStringBuilder; overload; function Append(const Value: Double): TStringBuilder; overload; function Append(const Value: Smallint): TStringBuilder; overload; function Append(const Value: Integer): TStringBuilder; overload; function Append(const Value: Int64): TStringBuilder; overload; function Append(const Value: TObject): TStringBuilder; overload; function Append(const Value: Shortint): TStringBuilder; overload; function Append(const Value: Single): TStringBuilder; overload; function Append(const Value: string): TStringBuilder; overload; function Append(const Value: UInt64): TStringBuilder; overload; function Append(const Value: TCharArray): TStringBuilder; overload; function Append(const Value: Word): TStringBuilder; overload; function Append(const Value: Cardinal): TStringBuilder; overload; {$IFNDEF NEXTGEN} function Append(const Value: PAnsiChar): TStringBuilder; overload; function Append(const Value: RawByteString): TStringBuilder; overload; {$ENDIF !NEXTGEN} function Append(const Value: Char; RepeatCount: Integer): TStringBuilder; overload; function Append(const Value: TCharArray; StartIndex: Integer; CharCount: Integer): TStringBuilder; overload; function Append(const Value: string; StartIndex: Integer; Count: Integer = -1): TStringBuilder; overload; function Append(const Value: PChar; StartIndex: Integer; Count: Integer = -1): TStringBuilder; overload; function AppendFormat(const Format: string; const Args: array of const): TStringBuilder; overload; function AppendLine: TStringBuilder; overload; inline; function AppendLine(const Value: string): TStringBuilder; overload; function AppendLineFormat(const Format: string; const Args: array of const): TStringBuilder; overload; procedure Clear; procedure CopyTo(SourceIndex: Integer; const Destination: TCharArray; DestinationIndex: Integer; Count: Integer); function EnsureCapacity(aCapacity: Integer): Integer; function Equals(StringBuilder: TStringBuilder): Boolean; reintroduce; function Insert(Index: Integer; const Value: Boolean): TStringBuilder; overload; function Insert(Index: Integer; const Value: Byte): TStringBuilder; overload; function Insert(Index: Integer; const Value: Char): TStringBuilder; overload; function Insert(Index: Integer; const Value: Currency): TStringBuilder; overload; function Insert(Index: Integer; const Value: Double): TStringBuilder; overload; function Insert(Index: Integer; const Value: Smallint): TStringBuilder; overload; function Insert(Index: Integer; const Value: Integer): TStringBuilder; overload; function Insert(Index: Integer; const Value: TCharArray): TStringBuilder; overload; function Insert(Index: Integer; const Value: Int64): TStringBuilder; overload; function Insert(Index: Integer; const Value: TObject): TStringBuilder; overload; function Insert(Index: Integer; const Value: Shortint): TStringBuilder; overload; function Insert(Index: Integer; const Value: Single): TStringBuilder; overload; function Insert(Index: Integer; const Value: string): TStringBuilder; overload; function Insert(Index: Integer; const Value: Word): TStringBuilder; overload; function Insert(Index: Integer; const Value: Cardinal): TStringBuilder; overload; function Insert(Index: Integer; const Value: UInt64): TStringBuilder; overload; function Insert(Index: Integer; const Value: string; count: Integer): TStringBuilder; overload; function Insert(Index: Integer; const Value: TCharArray; startIndex: Integer; charCount: Integer): TStringBuilder; overload; function Remove(StartIndex: Integer; RemLength: Integer): TStringBuilder; function Replace(const OldChar: Char; const NewChar: Char): TStringBuilder; overload; function Replace(const OldValue: string; const NewValue: string): TStringBuilder; overload; function Replace(const OldChar: Char; const NewChar: Char; StartIndex: Integer; Count: Integer): TStringBuilder; overload; function Replace(const OldValue: string; const NewValue: string; StartIndex: Integer; Count: Integer): TStringBuilder; overload; function ToString: string; overload; function ToString(UpdateCapacity: Boolean): string; reintroduce; overload; function ToString(StartIndex: Integer; StrLength: Integer): string; reintroduce; overload; function GetEnumerator: TEnumerator; inline; property Capacity: Integer read GetCapacity write SetCapacity; property Chars[index: Integer]: Char read GetChars write SetChars; default; property Length: Integer read GetLength write SetLength; property MaxCapacity: Integer read GetMaxCapacity; end; TManagedStringBuilder = TStringBuilder; TMSB = TStringBuilder; //function NewManagedStringBuilder():TManagedStringBuilder; overload; function NewManagedStringBuilder(aCapacity: Integer):TManagedStringBuilder; overload; function NewManagedStringBuilder(const Value: string):TManagedStringBuilder; overload; function NewManagedStringBuilder(aCapacity: Integer; aMaxCapacity: Integer):TManagedStringBuilder; overload; function NewManagedStringBuilder(const Value: string; aCapacity: Integer):TManagedStringBuilder; overload; function NewManagedStringBuilder(const Value: string; StartIndex: Integer; Length: Integer; aCapacity: Integer):TManagedStringBuilder; overload; implementation { function NewManagedStringBuilder():TManagedStringBuilder; begin Result := NewManagedStringBuilder(MaxInt); end; } function NewManagedStringBuilder(aCapacity: Integer):TManagedStringBuilder; begin Result := TManagedStringBuilder.Create(aCapacity); end; function NewManagedStringBuilder(const Value: string):TManagedStringBuilder; begin Result := TManagedStringBuilder.Create(Value); end; function NewManagedStringBuilder(aCapacity: Integer; aMaxCapacity: Integer):TManagedStringBuilder; begin Result := TManagedStringBuilder.Create(aCapacity, aMaxCapacity); end; function NewManagedStringBuilder(const Value: string; aCapacity: Integer):TManagedStringBuilder; begin Result := TManagedStringBuilder.Create(Value, aCapacity); end; function NewManagedStringBuilder(const Value: string; StartIndex: Integer; Length: Integer; aCapacity: Integer):TManagedStringBuilder; begin Result := TManagedStringBuilder.Create(Value, StartIndex, Length, aCapacity); end; { TStringBuilder } {$ZEROBASEDSTRINGS ON} function TStringBuilder.GetLength: Integer; begin Result := FLength; end; function TStringBuilder.GetCapacity: Integer; begin Result := System.Length(FData); end; function TStringBuilder.GetMaxCapacity: Integer; begin Result := FMaxCapacity; end; function TStringBuilder.Append(const Value: UInt64): TStringBuilder; begin Result := Append(UIntToStr(Value)); end; function TStringBuilder.Append(const Value: TCharArray): TStringBuilder; var I: Integer; begin I := 0; while (I < System.Length(Value)) and (Value[I] <> #0) do Inc(I); Result := Append(Value, 0, I); end; function TStringBuilder.Append(const Value: Single): TStringBuilder; begin Result := Append(FloatToStr(Value)); end; function TStringBuilder.Append(const Value: string): TStringBuilder; var Delta, OldLength: Integer; begin Delta := System.Length(Value); if Delta <> 0 then begin OldLength := Length; FLength := Length + Delta; if Length > Capacity then ExpandCapacity; Move(Pointer(Value)^, (PChar(Pointer(FData)) + OldLength)^, Delta * SizeOf(Char)); end; Result := self; end; function TStringBuilder.Append(const Value: Word): TStringBuilder; begin Result := Append(IntToStr(Value)); end; function TStringBuilder.Append(const Value: TCharArray; StartIndex, CharCount: Integer): TStringBuilder; var OldLength: Integer; begin if StartIndex + CharCount > System.Length(Value) then raise ERangeError.CreateResFmt(@SListIndexError, [StartIndex]); if StartIndex < 0 then raise ERangeError.CreateResFmt(@SListIndexError, [StartIndex]); if CharCount > 0 then begin OldLength := Length; Length := Length + CharCount; Move(Value[StartIndex], FData[OldLength], CharCount * SizeOf(Char)); end; Result := self; end; function TStringBuilder.Append(const Value: string; StartIndex, Count: Integer): TStringBuilder; var OldLength: Integer; begin if Count < 0 then Count := System.Length(Value) - StartIndex; if StartIndex + Count > System.Length(Value) then raise ERangeError.CreateResFmt(@SListIndexError, [StartIndex]); if StartIndex < 0 then raise ERangeError.CreateResFmt(@SListIndexError, [StartIndex]); if Count > 0 then begin OldLength := Length; Length := Length + Count; Move(Value[StartIndex + Low(string)], FData[OldLength], Count * SizeOf(Char)); end; Result := Self; end; function TStringBuilder.Append(const Value: PChar; StartIndex, Count: Integer): TStringBuilder; var OldLength: Integer; begin if Count < 0 then Count := Integer(StrLen(Value)) - StartIndex; if StartIndex < 0 then raise ERangeError.CreateResFmt(@SListIndexError, [StartIndex]); if Count > 0 then begin OldLength := Length; Length := Length + Count; Move((Value + StartIndex)^, FData[OldLength], Count * SizeOf(Char)); end; Result := Self; end; {$IFNDEF NEXTGEN} function TStringBuilder.Append(const Value: PAnsiChar): TStringBuilder; begin Result := Append(string(Value)); end; function TStringBuilder.Append(const Value: RawByteString): TStringBuilder; begin Result := Append(string(Value)); end; {$ENDIF !NEXTGEN} function TStringBuilder.Append(const Value: Cardinal): TStringBuilder; begin Result := Append(UIntToStr(Value)); end; function TStringBuilder.Append(const Value: Char; RepeatCount: Integer): TStringBuilder; begin Result := Append(System.StringOfChar(Value, RepeatCount)); end; function TStringBuilder.Append(const Value: Shortint): TStringBuilder; begin Result := Append(IntToStr(Value)); end; function TStringBuilder.Append(const Value: Char): TStringBuilder; begin FLength := Length + 1; if Length > Capacity then ExpandCapacity; FData[Length - 1] := Value; Result := Self; end; function TStringBuilder.Append(const Value: Currency): TStringBuilder; begin Result := Append(CurrToStr(Value)); end; function TStringBuilder.Append(const Value: Boolean): TStringBuilder; begin Result := Append(BoolToStr(Value, True)); end; function TStringBuilder.Append(const Value: Byte): TStringBuilder; begin Result := Append(IntToStr(Value)); end; function TStringBuilder.Append(const Value: Double): TStringBuilder; begin Result := Append(FloatToStr(Value)); end; function TStringBuilder.Append(const Value: Int64): TStringBuilder; begin Result := Append(IntToStr(Value)); end; function TStringBuilder.Append(const Value: TObject): TStringBuilder; begin {$if CompilerVersion >= 19} Result := Append(Value.ToString()); {$else} Result := Append(IntToStr(Integer(Value))); {$ENDIF} end; function TStringBuilder.Append(const Value: Smallint): TStringBuilder; begin Result := Append(IntToStr(Value)); end; function TStringBuilder.Append(const Value: Integer): TStringBuilder; begin Result := Append(IntToStr(Value)); end; function TStringBuilder.AppendFormat(const Format: string; const Args: array of const): TStringBuilder; begin Result := Append(System.SysUtils.Format(Format, Args)); end; function TStringBuilder.AppendLineFormat(const Format: string; const Args: array of const): TStringBuilder; begin Result := AppendLine(System.SysUtils.Format(Format, Args)); end; function TStringBuilder.AppendLine: TStringBuilder; begin Result := Append(LineBreak); end; function TStringBuilder.AppendLine(const Value: string): TStringBuilder; begin Result := Append(Value).AppendLine; end; procedure TStringBuilder.Clear; begin Length := 0; Capacity := DefaultCapacity; end; procedure TStringBuilder.CheckBounds(Index: Integer); begin if Cardinal(Index) >= Cardinal(Length) then raise ERangeError.CreateResFmt(@SListIndexError, [Index]); end; procedure TStringBuilder.CopyTo(SourceIndex: Integer; const Destination: TCharArray; DestinationIndex, Count: Integer); begin if Count < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['Count']); // DO NOT LOCALIZE if DestinationIndex < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['DestinationIndex']); // DO NOT LOCALIZE if DestinationIndex + Count > System.Length(Destination) then raise ERangeError.CreateResFmt(@SInputBufferExceed, ['DestinationIndex', DestinationIndex, 'Count', Count]); if Count > 0 then begin CheckBounds(SourceIndex); CheckBounds(SourceIndex + Count - 1); Move(FData[SourceIndex], Destination[DestinationIndex], Count * SizeOf(Char)); end; end; //constructor TStringBuilder.Create; class operator TStringBuilder.Initialize(out Dest: TStringBuilder); begin Dest.FLength := 0; //record不是class,FLength不会自动初始化为0,所以要先初始化FLength Dest.FMaxCapacity := MaxInt; Dest.Capacity := DefaultCapacity; end; class operator TStringBuilder.Finalize(var Dest: TStringBuilder); begin //暂时没有需要释放的 end; constructor TStringBuilder.Create(const Value: string; aCapacity: Integer); begin FMaxCapacity := MaxInt; Capacity := aCapacity; FLength := 0; Append(Value); end; constructor TStringBuilder.Create(const Value: string; StartIndex, length, aCapacity: Integer); begin Create(Value.Substring(StartIndex, length), aCapacity); end; constructor TStringBuilder.Create(aCapacity, aMaxCapacity: Integer); begin if aMaxCapacity <= 0 then raise ERangeError.CreateRes(@SRangeError); if aCapacity > aMaxCapacity then raise ERangeError.CreateResFmt(@SListCapacityError, [aCapacity]); Create(aCapacity); FMaxCapacity := aMaxCapacity; end; constructor TStringBuilder.Create(aCapacity: Integer); begin FMaxCapacity := MaxInt; Capacity := aCapacity; FLength := 0; end; constructor TStringBuilder.Create(const Value: string); begin Append(Value); end; procedure TStringBuilder.Free(); begin end; function TStringBuilder.EnsureCapacity(aCapacity: Integer): Integer; begin if Cardinal(aCapacity) > Cardinal(MaxCapacity) then raise ERangeError.CreateResFmt(@SListIndexError, [aCapacity]); if Capacity < aCapacity then Capacity := aCapacity; Result := Capacity; end; function TStringBuilder.Equals(StringBuilder: TStringBuilder): Boolean; begin Result := (Length = StringBuilder.Length) and (MaxCapacity = StringBuilder.MaxCapacity) and ((Length = 0) or CompareMem(Pointer(FData), Pointer(StringBuilder.FData), Length * SizeOf(Char))); end; procedure TStringBuilder.ExpandCapacity; var NewCapacity: Integer; begin NewCapacity := (Capacity * 3) div 2; if Length > NewCapacity then NewCapacity := Length * 2; // this line may overflow NewCapacity to a negative value if NewCapacity > MaxCapacity then NewCapacity := MaxCapacity; if NewCapacity < 0 then // if NewCapacity has been overflowed NewCapacity := Length; Capacity := NewCapacity; end; function TStringBuilder.GetChars(Index: Integer): Char; begin if Index < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['Index']); // DO NOT LOCALIZE CheckBounds(Index); Result := FData[Index]; end; function TStringBuilder.Insert(Index: Integer; const Value: TObject): TStringBuilder; begin {$if CompilerVersion >= 19} Result := Insert(Index, Value.ToString()); {$else} Result := Insert(Index, IntToStr(Integer(Value))); {$ENDIF} end; function TStringBuilder.Insert(Index: Integer; const Value: Int64): TStringBuilder; begin Result := Insert(Index, IntToStr(Value)); end; function TStringBuilder.Insert(Index: Integer; const Value: Single): TStringBuilder; begin Result := Insert(Index, FloatToStr(Value)); end; function TStringBuilder.Insert(Index: Integer; const Value: string): TStringBuilder; var OldLength: Integer; begin if Index < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['Index']); // DO NOT LOCALIZE if Index > Length then raise ERangeError.CreateResFmt(@SListIndexError, [Index]); OldLength := Length; Length := Length + System.Length(Value); if OldLength > Index then Move(FData[Index], FData[Index + System.Length(Value)], (OldLength - Index) * SizeOf(Char)); Move(Value[Low(string)], FData[Index], System.Length(Value) * SizeOf(Char)); Result := Self; end; function TStringBuilder.Insert(Index: Integer; const Value: Word): TStringBuilder; begin Result := Insert(Index, IntToStr(Value)); end; function TStringBuilder.Insert(Index: Integer; const Value: Shortint): TStringBuilder; begin Result := Insert(Index, IntToStr(Value)); end; function TStringBuilder.Insert(Index: Integer; const Value: TCharArray): TStringBuilder; var OldLength: Integer; begin if Index < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['Index']); // DO NOT LOCALIZE if Index > Length then raise ERangeError.CreateResFmt(@SListIndexError, [Index]); OldLength := Length; Length := Length + System.Length(Value); if OldLength > 0 then Move(FData[Index], FData[Index + System.Length(Value)], OldLength * SizeOf(Char)); Move(Value[0], FData[Index], System.Length(Value) * SizeOf(Char)); Result := Self; end; function TStringBuilder.Insert(Index: Integer; const Value: Currency): TStringBuilder; begin Result := Insert(Index, CurrToStr(Value)); end; function TStringBuilder.Insert(Index: Integer; const Value: Char): TStringBuilder; var OldLength: Integer; begin if Index < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['Index']); // DO NOT LOCALIZE if Index > Length then raise ERangeError.CreateResFmt(@SListIndexError, [Index]); OldLength := Length; Length := Length + 1; if OldLength > Index then Move(FData[Index], FData[Index + 1], (OldLength - Index) * SizeOf(Char)); FData[Index] := Value; Result := Self; end; function TStringBuilder.Insert(Index: Integer; const Value: Byte): TStringBuilder; begin Result := Insert(Index, IntToStr(Value)); end; function TStringBuilder.Insert(Index: Integer; const Value: Double): TStringBuilder; begin Result := Insert(Index, FloatToStr(Value)); end; function TStringBuilder.Insert(Index: Integer; const Value: Integer): TStringBuilder; begin Result := Insert(Index, IntToStr(Value)); end; function TStringBuilder.Insert(Index: Integer; const Value: Smallint): TStringBuilder; begin Result := Insert(Index, IntToStr(Value)); end; function TStringBuilder.Insert(Index: Integer; const Value: Boolean): TStringBuilder; begin Result := Insert(Index, BoolToStr(Value, True)); end; function TStringBuilder.Insert(Index: Integer; const Value: string; Count: Integer): TStringBuilder; var I: Integer; begin for I := 0 to Count - 1 do Insert(Index, Value); Result := Self; end; function TStringBuilder.Insert(Index: Integer; const Value: TCharArray; StartIndex, CharCount: Integer): TStringBuilder; var OldLength: Integer; begin if Index - 1 >= Length then raise ERangeError.CreateResFmt(@SListIndexError, [Index]) else if Index < 0 then raise ERangeError.CreateResFmt(@SListIndexError, [Index]); if StartIndex < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['StartIndex']); // DO NOT LOCALIZE if CharCount < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['CharCount']); // DO NOT LOCALIZE if StartIndex + CharCount > System.Length(Value) then raise ERangeError.CreateResFmt(@SInputBufferExceed, ['StartIndex', StartIndex, 'CharCount', CharCount]); OldLength := Length; Length := Length + CharCount; if OldLength > Index then Move(FData[Index], FData[Index + CharCount], (OldLength - Index) * SizeOf(Char)); Move(Value[StartIndex], FData[Index], CharCount * SizeOf(Char)); Result := Self; end; function TStringBuilder.Insert(Index: Integer; const Value: Cardinal): TStringBuilder; begin Result := Insert(Index, IntToStr(Value)); end; function TStringBuilder.Insert(Index: Integer; const Value: UInt64): TStringBuilder; begin Result := Insert(Index, UIntToStr(Value)); end; procedure TStringBuilder.ReduceCapacity; var NewCapacity: Integer; begin if Length > Capacity div 4 then Exit; NewCapacity := Capacity div 2; if NewCapacity < Length then NewCapacity := Length; Capacity := NewCapacity; end; function TStringBuilder.Remove(StartIndex, RemLength: Integer): TStringBuilder; begin if RemLength <> 0 then begin if StartIndex < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['StartIndex']); // DO NOT LOCALIZE if RemLength < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['RemLength']); // DO NOT LOCALIZE CheckBounds(StartIndex); CheckBounds(StartIndex + RemLength - 1); if (Length - (StartIndex + RemLength)) > 0 then Move(FData[StartIndex + RemLength], FData[StartIndex], (Length - (StartIndex + RemLength)) * SizeOf(Char)); Length := Length - RemLength; ReduceCapacity; end; Result := Self; end; function TStringBuilder.Replace(const OldValue, NewValue: string; StartIndex, Count: Integer): TStringBuilder; var CurPtr: PChar; EndPtr: PChar; Index: Integer; EndIndex: Integer; OldLen, NewLen: Integer; begin Result := Self; if Count <> 0 then begin if StartIndex < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['StartIndex']); // DO NOT LOCALIZE if Count < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['Count']); // DO NOT LOCALIZE if StartIndex + Count > Length then raise ERangeError.CreateResFmt(@SInputBufferExceed, ['StartIndex', StartIndex, 'Count', Count]); OldLen := System.Length(OldValue); NewLen := System.Length(NewValue); Index := StartIndex; CurPtr := @FData[StartIndex]; EndIndex := StartIndex + Count - OldLen; EndPtr := @FData[EndIndex]; while CurPtr <= EndPtr do begin if CurPtr^ = OldValue[Low(string)] then begin if StrLComp(CurPtr, PChar(OldValue), OldLen) = 0 then begin if _Replace(Index, OldValue, NewValue) then begin CurPtr := @FData[Index]; EndPtr := @FData[EndIndex]; end; Inc(CurPtr, NewLen - 1); Inc(Index, NewLen - 1); Inc(EndPtr, NewLen - OldLen); Inc(EndIndex, NewLen - OldLen); end; end; Inc(CurPtr); Inc(Index); end; end; end; function TStringBuilder.Replace(const OldChar, NewChar: Char; StartIndex, Count: Integer): TStringBuilder; var Ptr: PChar; EndPtr: PChar; begin if Count <> 0 then begin if StartIndex < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['StartIndex']); // DO NOT LOCALIZE if Count < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['Count']); // DO NOT LOCALIZE CheckBounds(StartIndex); CheckBounds(StartIndex + Count - 1); EndPtr := @FData[StartIndex + Count - 1]; Ptr := @FData[StartIndex]; while Ptr <= EndPtr do begin if Ptr^ = OldChar then Ptr^ := NewChar; Inc(Ptr); end; end; Result := Self; end; function TStringBuilder.Replace(const OldChar, NewChar: Char): TStringBuilder; var Ptr: PChar; EndPtr: PChar; begin EndPtr := @FData[Length - 1]; Ptr := @FData[0]; while Ptr <= EndPtr do begin if Ptr^ = OldChar then Ptr^ := NewChar; Inc(Ptr); end; Result := Self; end; function TStringBuilder.Replace(const OldValue, NewValue: string): TStringBuilder; begin Result := Replace(OldValue, NewValue, 0, Length); end; procedure TStringBuilder.SetCapacity(Value: Integer); begin if Value < Length then raise ERangeError.CreateResFmt(@SListCapacityError, [Value]); if Value > FMaxCapacity then raise ERangeError.CreateResFmt(@SListCapacityError, [Value]); System.SetLength(FData, Value); end; procedure TStringBuilder.SetChars(Index: Integer; Value: Char); begin if Index < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['Index']); // DO NOT LOCALIZE CheckBounds(Index); FData[Index] := Value; end; procedure TStringBuilder.SetLength(Value: Integer); var LOldLength: Integer; begin if Value < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['Value']); // DO NOT LOCALIZE if Value > MaxCapacity then raise ERangeError.CreateResFmt(@SListCapacityError, [Value]); LOldLength := FLength; try FLength := Value; if FLength > Capacity then ExpandCapacity; except on E: EOutOfMemory do begin FLength := LOldLength; raise; end; end; end; function TStringBuilder.ToString: string; begin Result := ToString(False); end; function TStringBuilder.ToString(UpdateCapacity: Boolean): string; begin if Length = Capacity then Result := FData else if UpdateCapacity then begin System.SetLength(FData, Length); Result := FData; end else Result := FData.Substring(0, Length); end; function TStringBuilder.ToString(StartIndex, StrLength: Integer): string; begin if StrLength <> 0 then begin if StartIndex < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['StartIndex']); // DO NOT LOCALIZE if StrLength < 0 then raise ERangeError.CreateResFmt(@SParamIsNegative, ['StrLength']); // DO NOT LOCALIZE CheckBounds(StartIndex); CheckBounds(StartIndex + StrLength - 1); Result := FData.Substring(StartIndex, StrLength); end else Result := ''; end; function TStringBuilder._Replace(Index: Integer; const Old, New: string): Boolean; var OldLength: Integer; OldCapacity: Integer; SizeChange: Integer; begin Result := False; SizeChange := System.Length(New) - System.Length(Old); if SizeChange = 0 then begin Move(New[Low(string)], FData[Index], System.Length(New) * SizeOf(Char)); end else begin OldLength := Length; if SizeChange > 0 then begin OldCapacity := Capacity; Length := Length + SizeChange; if OldCapacity <> Capacity then Result := True; end; Move(FData[Index + System.Length(Old)], FData[Index + System.Length(New)], (OldLength - (System.Length(Old) + Index)) * SizeOf(Char)); Move(New[Low(String)], FData[Index], System.Length(New) * SizeOf(Char)); if SizeChange < 0 then Length := Length + SizeChange; end; end; { TStringBuilder.TEnumerator } procedure TStringBuilder.TEnumerator.Initialize(const ABuilder: TStringBuilder); begin FPtr := PChar(Pointer(ABuilder.FData)) - 1; FEndPtr := FPtr + ABuilder.Length; end; function TStringBuilder.TEnumerator.GetCurrent: Char; begin Result := FPtr^; end; function TStringBuilder.TEnumerator.MoveNext: Boolean; begin Inc(FPtr); Result := FPtr <= FEndPtr; end; function TStringBuilder.GetEnumerator: TEnumerator; begin Result.Initialize(Self); end; end.