Managed Record的使用一则

其实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.



此条目发表在Delphi分类目录,贴了, , 标签。将固定链接加入收藏夹。

发表评论

电子邮件地址不会被公开。

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

你必须启用JavaScript