unit VirtualTrees.Classes;

// The contents of this file are subject to the Mozilla Public License
// Version 1.1 (the "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
//
// Alternatively, you may redistribute this library, use and/or modify it under the terms of the
// GNU Lesser General Public License as published by the Free Software Foundation;
// either version 2.1 of the License, or (at your option) any later version.
// You may obtain a copy of the LGPL at http://www.gnu.org/copyleft/.
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
// specific language governing rights and limitations under the License.
//
// The original code is VirtualTrees.pas, released September 30, 2000.
//
// The initial developer of the original code is digital publishing AG (Munich, Germany, www.digitalpublishing.de),
// written by Mike Lischke (public@soft-gems.net, www.soft-gems.net).
//
// Portions created by digital publishing AG are Copyright
// (C) 1999-2001 digital publishing AG. All Rights Reserved.
//----------------------------------------------------------------------------------------------------------------------

interface

{$WARN UNSAFE_TYPE OFF}
{$WARN UNSAFE_CAST OFF}
{$WARN UNSAFE_CODE OFF}

uses
  Winapi.Windows;

type
  // Helper classes to speed up rendering text formats for clipboard and drag'n drop transfers.
  TBufferedRawByteString = class
  private
    FStart,
    FPosition,
    FEnd: PAnsiChar;
    function GetAsString: RawByteString;
  public
    destructor Destroy; override;

    procedure Add(const S: RawByteString);
    procedure AddNewLine;

    property AsString: RawByteString read GetAsString;
  end;

  TBufferedString = class
  private
    FStart,
    FPosition,
    FEnd: PWideChar;
    function GetAsString: string;
  public
    destructor Destroy; override;

    procedure Add(const S: string);
    procedure AddNewLine;

    property AsString: string read GetAsString;
  end;


implementation

//----------------- TBufferedRawByteString ------------------------------------------------------------------------------------

const
  AllocIncrement = 2 shl 11;  // Must be a power of 2.

destructor TBufferedRawByteString.Destroy;

begin
  FreeMem(FStart);
  inherited;
end;

//----------------------------------------------------------------------------------------------------------------------

function TBufferedRawByteString.GetAsString: RawByteString;

begin
  SetString(Result, FStart, FPosition - FStart);
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TBufferedRawByteString.Add(const S: RawByteString);

var
  NewLen,
  LastOffset,
  Len: NativeInt;

begin
  Len := Length(S);
  // Make room for the new string.
  if FEnd - FPosition <= Len then
  begin
    // Round up NewLen so it is always a multiple of AllocIncrement.
    NewLen := FEnd - FStart + (Len + AllocIncrement - 1) and not (AllocIncrement - 1);
    // Keep last offset to restore it correctly in the case that FStart gets a new memory block assigned.
    LastOffset := FPosition - FStart;
    ReallocMem(FStart, NewLen);
    FPosition := FStart + LastOffset;
    FEnd := FStart + NewLen;
  end;
  Move(PAnsiChar(S)^, FPosition^, Len);
  Inc(FPosition, Len);
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TBufferedRawByteString.AddNewLine;

var
  NewLen,
  LastOffset: NativeInt;

begin
  // Make room for the CR/LF characters.
  if FEnd - FPosition <= 2 then
  begin
    // Round up NewLen so it is always a multiple of AllocIncrement.
    NewLen := FEnd - FStart + (2 + AllocIncrement - 1) and not (AllocIncrement - 1);
    // Keep last offset to restore it correctly in the case that FStart gets a new memory block assigned.
    LastOffset := FPosition - FStart;
    ReallocMem(FStart, NewLen);
    FPosition := FStart + LastOffset;
    FEnd := FStart + NewLen;
  end;
  FPosition^ := #13;
  Inc(FPosition);
  FPosition^ := #10;
  Inc(FPosition);
end;

//----------------- TBufferedString --------------------------------------------------------------------------------

destructor TBufferedString.Destroy;

begin
  FreeMem(FStart);
  inherited;
end;

//----------------------------------------------------------------------------------------------------------------------

function TBufferedString.GetAsString: string;

begin
  SetString(Result, FStart, FPosition - FStart);
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TBufferedString.Add(const S: string);

var
  NewLen,
  LastOffset,
  Len: Integer;

begin
  Len := Length(S);
  if Len = 0 then
    exit;//Nothing to do
  // Make room for the new string.
  if FEnd - FPosition <= Len then
  begin
    // Round up NewLen so it is always a multiple of AllocIncrement.
    NewLen := FEnd - FStart + (Len + AllocIncrement - 1) and not (AllocIncrement - 1);
    // Keep last offset to restore it correctly in the case that FStart gets a new memory block assigned.
    LastOffset := FPosition - FStart;
    ReallocMem(FStart, 2 * NewLen);
    FPosition := FStart + LastOffset;
    FEnd := FStart + NewLen;
  end;
  Move(PWideChar(S)^, FPosition^, 2 * Len);
  Inc(FPosition, Len);
end;

//----------------------------------------------------------------------------------------------------------------------

procedure TBufferedString.AddNewLine;

var
  NewLen,
  LastOffset: Integer;

begin
  // Make room for the CR/LF characters.
  if FEnd - FPosition <= 4 then
  begin
    // Round up NewLen so it is always a multiple of AllocIncrement.
    NewLen := FEnd - FStart + (2 + AllocIncrement - 1) and not (AllocIncrement - 1);
    // Keep last offset to restore it correctly in the case that FStart gets a new memory block assigned.
    LastOffset := FPosition - FStart;
    ReallocMem(FStart, 2 * NewLen);
    FPosition := FStart + LastOffset;
    FEnd := FStart + NewLen;
  end;
  FPosition^ := #13;
  Inc(FPosition);
  FPosition^ := #10;
  Inc(FPosition);
end;


end.
