
{ Copyright (c) 1989,90 by Borland International, Inc. }

unit TCLStr;
{ Turbo Pascal 6.0 object-oriented example long string routines.
  This unit is used by TCALC.PAS.
  See TCALC.DOC for an more information about this example.
}

{$S-}

interface

uses Objects, TCUtil;

const
  MaxLStringLength = 65521;  { The maximum amount that can be allocated
                               to a pointer }

type
  LStringRange = 0..MaxLStringLength;
  LStringData = array [1..MaxLStringLength] of Char;
  LStringDataPtr = ^LStringData;
  LStringPtr = ^LString;
  LString = object
    Len : LStringRange;        { Current length }
    MaxLen : LStringRange;     { Length that has been allocated.  This is
                                 always allocated in blocks of 16 bytes so
                                 that the long string's data doesn't have to 
                                 be reallocated every time the long string 
                                 grows }
    Data : LStringDataPtr;
    constructor Init;
    destructor Done;
    function SetValue(NewLen : LStringRange; NewData : Pointer) : Boolean;
    function FromString(S : String) : Boolean;
    function ToString : String;
    function Length : LStringRange;
    function Copy(Start, Amt : LStringRange) : String;
    function Insert(S : String; Start : LStringRange) : Boolean;
    procedure Delete(Start, Amt : LStringRange);
    function Append(S : String) : Boolean;
    procedure Change(Ch : Char; Start : LStringRange);
    function Assign(LS : LString) : Boolean;
    function FromStream(var S : TDosStream) : Boolean;
    procedure ToStream(var S : TDosStream);
  end;

implementation

constructor LString.Init;
{ Initializes the long string. }
begin
  Len := 0;
  MaxLen := 0;
  Data := nil;
end; { LString.Init }

destructor LString.Done;
{ Frees memory used by the long string. }
begin
  if Data <> nil then
    FreeMem(Data, MaxLen);
end; { LString.Done }

function LString.SetValue(NewLen : LStringRange;
                          NewData : Pointer) : Boolean;
{ Copies an area of memory to the long string }
var
  Size : Word;
  NData : Pointer;
begin
  Size := (NewLen + 15) shr 4 shl 4;   { Calculate the new size }
  if NewLen > MaxLen then        { Allocate new data area if the long string }
  begin                          { needs to grow }
    GetMem(NData, Size);
    if NData = nil then          { The allocation failed.  Return False }
    begin
      SetValue := False;
      Exit;
    end;
    if Data <> nil then          { If there was any data in the long string, }
    begin                        { copy it to the new data area }
      Move(Data^, NData^, Len);
      FreeMem(Data, MaxLen);     { Free the memory used by the long string }
    end;                         { before it was reallocated }
    Data := NData;               { Set Data and MaxLen to their new values }
    MaxLen := Size;
  end;
  Move(NewData^, Data^, NewLen); { Copy the new data to the long string }
  Len := NewLen;                 { Set the length }
  SetValue := True;              { Successful - Return True }
end; { LString.SetValue }

function LString.FromString(S : String) : Boolean;
{ Converts a string into a long string }
begin
  if not SetValue(System.Length(S), @S[1]) then
  begin                              { Set the long string to be a null }
    FromString := SetValue(0, nil);  { string if it could not be expanded }
    FromString := False;             { Return False }
  end
  else
    FromString := True;              { Successful.  Return True }
end; { LString.FromString }

function LString.ToString : String;
{ Converts a long string into a string }
var
  S : String;
  NewLen : Byte;
begin
  NewLen := Min(255, Length);   { The maximum length of a string is 255 }
  S[0] := Chr(NewLen);          { Set the length of the new string }
  Move(Data^, S[1], NewLen);    { Copy the data }
  ToString := S;                { Return the new string }
end; { LString.ToString }

function LString.Length : LStringRange;
{ Returns the current length of a long string }
begin
  Length := Len;
end; { LString.Length }

function LString.Copy(Start, Amt : LStringRange) : String;
{ Copies part of a long string into a string }
var
  S : String;
begin
  if Start > Len then      { Trying to copy past the end of the long }
    Amt := 0               { string - return a null string }
  else
    Amt := Min(Amt, Succ(Len - Start));   { Calculate length of new string }
  S[0] := Chr(Amt);                  { Set length of new string }
  Move(Data^[Start], S[1], Amt);     { Copy data into new string }
  Copy := S;                         { Return new string }
end; { LString.Copy }

function LString.Insert(S : String; Start : LStringRange) : Boolean;
{ Inserts a string into a long string }
var
  OldLen : LStringRange;
  Size : Word;
  NData : Pointer;
begin
  OldLen := Len;
  Inc(Len, System.Length(S));
  if Len > MaxLen then               { Allocate new data area if the long }
  begin                              { string needs to grow }
    Size := (Len + 15) shr 4 shl 4;  { Calculate the new size }
    GetMem(NData, Size);             { Allocate new data area }
    if NData = nil then              { The long string could not be expanded }
    begin
      Dec(Len, System.Length(S));    { Restore the old Len value }
      Insert := False;               { Return False }
      Exit;
    end;
    if Data <> nil then              { If there was data in the long string, }
    begin                            { copy it to the new data area }
      Move(Data^, NData^, OldLen);
      FreeMem(Data, MaxLen);         { Free the old data area }
    end;
    Data := NData;                   { Set new values for Data and MaxLen }
    MaxLen := Size;
  end;
  if Start <= OldLen then  { Move the part of the string after the insert to }
                           { the right to make space for the new string }
    Move(Data^[Start], Data^[Start + System.Length(S)], Succ(OldLen - Start));
  Move(S[1], Data^[Start], System.Length(S));   { Insert the new string }
  Insert := True;                { Successful - return True }
end; { LString.Insert }

procedure LString.Delete(Start, Amt : LStringRange);
{ Deletes part of a long string }
begin
  Amt := Min(Amt, Succ(Len - Start));  { No characters can be deleted past 
                                         the end of the long string }
  if Start + Amt <= Len then  { The delete is in the middle of the long
                                string - move the rest of the data to the
                                left }
    Move(Data^[Start + Amt], Data^[Start], Succ(Len - Amt - Start));
  Dec(Len, Amt);              { Fix the length value }
end; { LString.Delete }

function LString.Append(S : String) : Boolean;
{ Appends a string to a long string }
begin
  Append := Insert(S, Succ(Len));   { Insert the string at the end }
end; { LString.Append }

procedure LString.Change(Ch : Char; Start : LStringRange);
{ Change a particular character of a long string }
begin
  Move(Ch, Data^[Start], 1);
end; { LString.Change }

function LString.Assign(LS : LString) : Boolean;
{ Copy one long string to another one }
begin
  Assign := SetValue(LS.Length, LS.Data);
end; { LString.Assign }

function LString.FromStream(var S : TDosStream) : Boolean;
{ Read a long string from a stream }
var
  Counter, NewLen, Size : Word;
  Dummy : Byte;
  NData : Pointer;
begin
  S.Read(NewLen, SizeOf(NewLen));     { Read the length }
  Size := (NewLen + 15) shr 4 shl 4;  { Calculate the new size }
  if NewLen > MaxLen then       { Allocate new data area if the long string }
  begin                         { needs to grow }
    GetMem(NData, Size);
    if NData = nil then         { The allocation failed.  Return False }
    begin
      for Counter := 1 to NewLen do  { Read the string in so that the file }
        S.Read(Dummy, 1);            { position is still correct }
      FromStream := False;
      Exit;
    end;
    if Data <> nil then         { If there was any data in the long string, }
    begin                       { copy it to the new data area }
      Move(Data^, NData^, Len);
      FreeMem(Data, MaxLen);
    end;
    Data := NData;              { Set new values for Data and MaxLen }
    MaxLen := Size;
  end;
  S.Read(Data^, NewLen);        { Read the long string from the stream }
  Len := NewLen;
  FromStream := True;           { Successful - return True }
end; { LString.FromStream }

procedure LString.ToStream(var S : TDosStream);
{ Write a long string to a stream }
begin
  S.Write(Len, SizeOf(Len));    { Write the length }
  S.Write(Data^, Len);          { Write the long string }
end; { LString.ToStream }

end.
