{ +-------------------------------------------------------------+ } { | | } { | GM-Software | } { | =========== | } { | | } { | Project: All Projects | } { | | } { | Description: String builder that speeds up assembling | } { | large string from smaller pieces. | } { | | } { | Copyright (C) - 2024 - Gerrit Moeller. | } { | | } { | Source code distributed under MIT license. | } { | | } { | See: https://www.gm-software.de | } { | | } { +-------------------------------------------------------------+ } {$INCLUDE GMCompilerSettings.inc} unit GMStrBuilder; interface uses GMStrDef; type {$if FPC_FULLVERSION < 30101} {$ERROR The string builder code needs FPC version >= 3.1.1. If you are using Delphi make sure that custom managed record operators are supported} {$endif} // // TStrType is meant to be a string data type like: UnicodeString, WideString, AnsiString, RawByteString, Utf8String or AnsiString(CP_XXXX) // RGMGenericStringBuilder<TStrType> = record private class operator Implicit(AValue: UnicodeString): RGMGenericStringBuilder<TStrType>; class operator Implicit(AValue: WideString): RGMGenericStringBuilder<TStrType>; class operator Implicit(AValue: AnsiString): RGMGenericStringBuilder<TStrType>; class operator Implicit(AValue: Utf8String): RGMGenericStringBuilder<TStrType>; class operator Implicit(const AStrBuilder: RGMGenericStringBuilder<TStrType>): UnicodeString; class operator Implicit(const AStrBuilder: RGMGenericStringBuilder<TStrType>): WideString; class operator Implicit(const AStrBuilder: RGMGenericStringBuilder<TStrType>): AnsiString; class operator Implicit(const AStrBuilder: RGMGenericStringBuilder<TStrType>): Utf8String; procedure Grow(AAddLen: Integer); public StrBufAllocAlign: Integer; Len: Integer; StrBuffer: TStrType; class operator Initialize(var AInstance: RGMGenericStringBuilder<TStrType>); class operator Add(AValue1: RGMGenericStringBuilder<TStrType>; AValue2: UnicodeString): RGMGenericStringBuilder<TStrType>; function Append(AStr: TStrType): Integer; overload; function Append(AChar: AnsiChar): Integer; overload; function Append(AChar: WideChar): Integer; overload; function Join(ASeparator, AStr: TStrType): Integer; procedure Clear(AFreeMemory: Boolean = False); //class operator Finalize(var AValue: RProgramMemory); end; RGMStringBuilder = RGMGenericStringBuilder<TGMString>; var vDfltStrBufAllocAlign: Integer = $2000; // <- 8 KiB implementation { ------------------------------------------- } { ---- RGMGenericStringBuilder<TStrType> ---- } { ------------------------------------------- } class operator RGMGenericStringBuilder<TStrType>.Initialize(var AInstance: RGMGenericStringBuilder<TStrType>); begin AInstance.Len := 0; AInstance.StrBuffer := ''; AInstance.StrBufAllocAlign := vDfltStrBufAllocAlign; end; class operator RGMGenericStringBuilder<TStrType>.Add(AValue1: RGMGenericStringBuilder<TStrType>; AValue2: UnicodeString): RGMGenericStringBuilder<TStrType>; begin AValue1.Append(AValue2); Result := AValue1; end; procedure RGMGenericStringBuilder<TStrType>.Grow(AAddLen: Integer); begin if Len + AAddLen > Length(StrBuffer) then begin SetLength(StrBuffer, Align(Len + AAddLen, StrBufAllocAlign)); end; end; function RGMGenericStringBuilder<TStrType>.Append(AStr: TStrType): Integer; var addLen: Integer; begin if Len <= 0 then begin StrBuffer := AStr; Len := Length(StrBuffer); end else begin addLen := Length(AStr); if addLen > 0 then begin Grow(addLen); System.Move(AStr[1], StrBuffer[Len+1], addLen * SizeOf(StrBuffer[1])); //for i:=1 to addLen do StrBuffer[Len + i] := AStr[i]; Len += addLen; end; end; Result := Len; end; function RGMGenericStringBuilder<TStrType>.Append(AChar: AnsiChar): Integer; begin Grow(1); StrBuffer[Len + 1] := AChar; Len += 1; Result := Len; end; function RGMGenericStringBuilder<TStrType>.Append(AChar: WideChar): Integer; begin Grow(1); StrBuffer[Len + 1] := AChar; Len += 1; Result := Len; end; function RGMGenericStringBuilder<TStrType>.Join(ASeparator, AStr: TStrType): Integer; begin if Length(AStr) > 0 then begin if Len <= 0 then Append(AStr) else Append(ASeparator + AStr); end; Result := Len; end; procedure RGMGenericStringBuilder<TStrType>.Clear(AFreeMemory: Boolean); begin if AFreeMemory then StrBuffer := ''; Len := 0; end; {$macro on} {$define CommonAssignFromStr:= Result.StrBuffer := AValue; Result.Len := Length(Result.StrBuffer);} class operator RGMGenericStringBuilder<TStrType>.Implicit(AValue: UnicodeString): RGMGenericStringBuilder<TStrType>; begin CommonAssignFromStr end; class operator RGMGenericStringBuilder<TStrType>.Implicit(AValue: WideString): RGMGenericStringBuilder<TStrType>; begin CommonAssignFromStr end; class operator RGMGenericStringBuilder<TStrType>.Implicit(AValue: AnsiString): RGMGenericStringBuilder<TStrType>; begin CommonAssignFromStr end; class operator RGMGenericStringBuilder<TStrType>.Implicit(AValue: Utf8String): RGMGenericStringBuilder<TStrType>; begin CommonAssignFromStr end; {$define CommonAssignToStr:= if (AStrBuilder.Len = Length(AStrBuilder.StrBuffer)) then Result := AStrBuilder.StrBuffer else Result := System.Copy(AStrBuilder.StrBuffer, 1, AStrBuilder.Len);} class operator RGMGenericStringBuilder<TStrType>.Implicit(const AStrBuilder: RGMGenericStringBuilder<TStrType>): UnicodeString; begin CommonAssignToStr end; class operator RGMGenericStringBuilder<TStrType>.Implicit(const AStrBuilder: RGMGenericStringBuilder<TStrType>): WideString; begin CommonAssignToStr end; class operator RGMGenericStringBuilder<TStrType>.Implicit(const AStrBuilder: RGMGenericStringBuilder<TStrType>): AnsiString; begin CommonAssignToStr end; class operator RGMGenericStringBuilder<TStrType>.Implicit(const AStrBuilder: RGMGenericStringBuilder<TStrType>): Utf8String; begin CommonAssignToStr end; end.