home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / acl-lib.zip / ACLString.pas < prev    next >
Pascal/Delphi Source File  |  2000-07-27  |  14KB  |  601 lines

  1. Unit ACLString;
  2.  
  3. Interface
  4.  
  5. uses
  6.   SysUtils;
  7.  
  8. {
  9.   AString: A length-safe string class. Internally maintains
  10.   a length as well as a zero terminator, so very fast for concatenation
  11.  
  12.   There is no point in this class for Delphi, which has a fast
  13.   reference counted string type built in.
  14.  
  15.   For extra safety it explicitly checks that this is a valid instance of
  16.   AString on every method call (using an internal magic number)
  17.   You can also call the global procedure
  18.     CheckAllAStringsDestroyed
  19.   at the end of the program to make sure there are no memory leaks
  20.   due to AStrings not being destroyed.
  21.  
  22.   V1.2 28/6/00
  23.     Added ReadLn and WriteLn methods (for text files)
  24.     Added Character index property
  25.   V1.1 27/6/00
  26.     Added:
  27.       Delete - delete a seciton of string
  28.       Assign methods
  29.       AsString property
  30.       CharPosition function
  31.       ExtractNextValue method
  32.         This method, unlike my original string version, does not
  33.         alter the main string. Rather it takes and increments
  34.         a starting position.
  35.  
  36.   V1.0
  37.     Completed basic functionality
  38.     Used in NewView for decoding help topics. Fast!
  39. }
  40.  
  41. type
  42.   EAStringError = class( Exception );
  43.   EAStringIndexError = class( EAStringError );
  44.  
  45.   TAString = class
  46.   private
  47.     function GetIsEmpty: boolean;
  48.   protected
  49.     _S: PChar;
  50.     _Length: longint;
  51.     _MagicNumber: longword;
  52.     procedure CheckSize( const NeededLength: longint );
  53.     procedure AddData( const Data: pointer; const DataLength: longint );
  54.     procedure Initialise;
  55.  
  56.     function ValidIndex( const Index: longint ): boolean;
  57.     procedure CheckIndex( const Index: longint );
  58.     function GetAsString: string;
  59.  
  60.     procedure SetLength( NewLength: longint );
  61.  
  62.     function GetChar( Index: longint ): Char;
  63.     procedure SetChar( Index: longint;
  64.                        const Value: Char );
  65.  
  66.   public
  67.  
  68.     constructor Create;
  69.     constructor CreateFrom( const S: String );
  70.     constructor CreateFromPChar( const S: PChar );
  71.     constructor CreateCopy( const S: TAString );
  72.  
  73.     // Create a AString from the given PChar and
  74.     // dispose of the PChar. Useful for using when you can only
  75.     // get a PChar as a newly allocated string (e.g TMemo.Lines.GetText)
  76.     constructor CreateFromPCharWithDispose( const S: PChar );
  77.  
  78.     destructor Destroy; override;
  79.  
  80.     // Modifications
  81.     procedure Assign( const S: TAString );
  82.     procedure AssignString( const S: string );
  83.     procedure AssignPChar( const S: PChar );
  84.  
  85.     procedure Add( const S: String );
  86.     procedure AddPChar( const S: PChar );
  87.     procedure AddAString( const S: TAString );
  88.  
  89.     procedure Trim;
  90.     procedure TrimChar( CharToTrim: Char );
  91.     procedure Delete( const StartingFrom: longint;
  92.                       const LengthToDelete: longint );
  93.     procedure Clear;
  94.  
  95.     // Properties
  96.     property AsPChar: PChar read _S;
  97.     property AsString: string read GetAsString;
  98.     property Character[ Index: longint ]: Char read GetChar write SetChar; default;
  99.     property Length: longint read _Length write SetLength;
  100.     property IsEmpty: boolean read GetIsEmpty;
  101.  
  102.     // Queries
  103.     function CharPosition( const StartingFrom: longint;
  104.                            const CharToFind: Char ): longint;
  105.  
  106.     function SameAs( S: String ): boolean;
  107.  
  108.     // Extract the next value seperated by seperator
  109.     // starting at StartingFrom (zero based index!)
  110.     procedure ExtractNextValue( Var StartingFrom: longint;
  111.                                 ExtractTo: TAString;
  112.                                 const Seperator: Char );
  113.     procedure GetRightFrom( const StartingFrom: longint;
  114.                             Dest: TAString );
  115.     procedure GetLeft( const Count: longint;
  116.                        Dest: TAString );
  117.     procedure GetRight( const Count: longint;
  118.                         Dest: TAString );
  119.     procedure ParseKeyValuePair( KeyName: TAString;
  120.                                  KeyValue: TAString;
  121.                                  Seperator: Char );
  122.  
  123.     // Read a line from the given file. Line must end
  124.     // with #13 #10. ( Single #13 or #10 not recognised )
  125.     procedure ReadLn( Var TheFile: TextFile );
  126.     procedure WriteLn( Var TheFile: TextFile );
  127.   end;
  128.  
  129. // call this to be sure all AStrings have been destroyed.
  130. procedure CheckAllAStringsDestroyed;
  131.  
  132. Implementation
  133.  
  134. uses
  135.   ACLUtility, ACLPCharUtility;
  136.  
  137. const
  138.   GlobalAStringCreatedCount: longint = 0;
  139.   GlobalAStringDestroyedCount: longint = 0;
  140.  
  141. const
  142.   MagicConstant = $cabba9e;
  143.  
  144. procedure CheckAllAStringsDestroyed;
  145. begin
  146.   if GlobalAStringCreatedCount > GlobalAStringDestroyedCount then
  147.     raise Exception.Create( 'Not all AStrings have been destroyed ('
  148.                             + IntToStr( GlobalAStringCreatedCount )
  149.                             + ' created, '
  150.                             + IntToStr( GlobalAStringDestroyedCount )
  151.                             + ' destroyed). Possible memory leak.' );
  152. end;
  153.  
  154. procedure CheckValid( const S: TAString );
  155. var
  156.   IsValid: boolean;
  157. begin
  158.   try
  159.     IsValid:= S._MagicNumber = MagicConstant;
  160.   except
  161.     IsValid:= false;
  162.   end;
  163.   if not IsValid then
  164.     raise Exception.Create( 'Reference to invalid AString' );
  165. end;
  166.  
  167. constructor TAString.Create;
  168. begin
  169.   inherited Create;
  170.   Initialise;
  171. end;
  172.  
  173. procedure TAString.Initialise;
  174. begin
  175.   inc( GlobalAStringCreatedCount );
  176.   _S:= StrAlloc( 16 );
  177.   _MagicNumber:= MagicConstant;
  178.   Clear;
  179. end;
  180.  
  181. constructor TAString.CreateFrom( const S: String );
  182. begin
  183.   Initialise;
  184.   AssignString( S );
  185. end;
  186.  
  187. constructor TAString.CreateFromPChar( const S: PChar );
  188. begin
  189.   Initialise;
  190.   AssignPChar( S );
  191. end;
  192.  
  193. constructor TAString.CreateFromPCharWithDispose( const S: PChar );
  194. begin
  195.   Initialise;
  196.   AddPChar( S );
  197.   StrDispose( S );
  198. end;
  199.  
  200. constructor TAString.CreateCopy( const S: TAString );
  201. begin
  202.   Initialise;
  203.   Assign( S );
  204. end;
  205.  
  206. destructor TAString.Destroy;
  207. begin
  208.   inc( GlobalAStringDestroyedCount );
  209.   StrDispose( _S );
  210.   _MagicNumber:= 0;
  211.   inherited Destroy;
  212. end;
  213.  
  214. procedure TAString.CheckSize( const NeededLength: longint );
  215. var
  216.   temp: PChar;
  217.   NewBufferSize: longint;
  218.   CurrentBufferSize: longint;
  219. begin
  220.   CurrentBufferSize:= StrBufSize( _S );
  221.   if NeededLength + 1 > CurrentBufferSize then
  222.   begin
  223.     // allocate new buffer, double the size...
  224.     NewBufferSize:= CurrentBufferSize * 2;
  225.     // or if that's not enough...
  226.     if NewBufferSize < NeededLength + 1 then
  227.       // double what we are going to need
  228.       NewBufferSize:= NeededLength * 2;
  229.  
  230.     temp:= StrAlloc( NewBufferSize );
  231.  
  232.     MemCopy( _S,
  233.              Temp,
  234.              _Length + 1 );
  235.  
  236.     StrDispose( _S );
  237.     _S:= temp;
  238.   end;
  239. end;
  240.  
  241. procedure TAString.Clear;
  242. begin
  243.   CheckValid( self );
  244.   _Length:= 0;
  245.   _S[ 0 ]:= #0;
  246. end;
  247.  
  248. procedure TAString.AddData( const Data: pointer; const DataLength: longint );
  249. begin
  250.   if DataLength = 0 then
  251.     exit;
  252.   CheckSize( _Length + DataLength );
  253.   MemCopy( Data, _S + _Length, DataLength );
  254.   inc( _Length, DataLength );
  255.   _S[ _Length ]:= #0;
  256. end;
  257.  
  258. procedure TAString.Add( const S: String );
  259. begin
  260.   CheckValid( self );
  261. {$ifdef os2}
  262.   AddData( Addr( S ) + 1, System.Length( S ) );
  263. {$else}
  264.   AddData( PChar( S ), System.Length( S ) );
  265. {$endif}
  266. end;
  267.  
  268. procedure TAString.AddPChar( const S: PChar );
  269. begin
  270.   CheckValid( self );
  271.   AddData( S, StrLen( S ) );
  272. end;
  273.  
  274. procedure TAString.AddAString( const S: TAString );
  275. begin
  276.   CheckValid( self );
  277.   CheckValid( S );
  278.   AddData( S._S, S.Length );
  279. end;
  280.  
  281. procedure TAString.TrimChar( CharToTrim: Char );
  282. var
  283.   StartP: PChar;
  284.   EndP: PChar;
  285.   C: Char;
  286. begin
  287.   CheckValid( self );
  288.   if _Length = 0 then
  289.     exit;
  290.   StartP:= _S;
  291.   EndP:= _S + Length;
  292.  
  293.   while StartP < EndP do
  294.   begin
  295.     C:= StartP^;
  296.     if C <> CharToTrim then
  297.       break;
  298.     inc( StartP );
  299.   end;
  300.   // StartP now points to first non-space char
  301.  
  302.   while EndP > StartP do
  303.   begin
  304.     dec( EndP );
  305.     C:= EndP^;
  306.     if C <> CharToTrim then
  307.     begin
  308.       inc( EndP );
  309.       break;
  310.     end;
  311.   end;
  312.   // EndP now points to one byte past last non-space char
  313.  
  314.   _Length:= PCharDiff( EndP, StartP );
  315.  
  316.   if _Length > 0 then
  317.     if StartP > _S then
  318.       MemCopy( StartP, _S, _Length );
  319.  
  320.   _S[ _Length ]:= #0;
  321.  
  322. end;
  323.  
  324. procedure TAString.ExtractNextValue( Var StartingFrom: longint;
  325.                                      ExtractTo: TAString;
  326.                                      const Seperator: Char );
  327. var
  328.   NextSeperatorPosition: longint;
  329. begin
  330.   CheckValid( self );
  331.   CheckValid( ExtractTo );
  332.  
  333.   ExtractTo.Clear;
  334.   if StartingFrom >= Length then
  335.     exit;
  336.   NextSeperatorPosition:= CharPosition( StartingFrom,
  337.                                         Seperator );
  338.   if NextSeperatorPosition > -1 then
  339.   begin
  340.     ExtractTo.AddData( _S + StartingFrom,
  341.                        NextSeperatorPosition - StartingFrom );
  342.     StartingFrom:= NextSeperatorPosition + 1;
  343.   end
  344.   else
  345.   begin
  346.     ExtractTo.AddData( _S + StartingFrom,
  347.                        Length - StartingFrom );
  348.     StartingFrom:= Length;
  349.   end;
  350.   ExtractTo.Trim;
  351.  
  352. end;
  353.  
  354. procedure TAString.Assign(const S: TAString);
  355. begin
  356.   Clear;
  357.   AddAString( S );
  358. end;
  359.  
  360. procedure TAString.AssignPChar(const S: PChar);
  361. begin
  362.   Clear;
  363.   AddPChar( S );
  364. end;
  365.  
  366. procedure TAString.AssignString(const S: string);
  367. begin
  368.   Clear;
  369.   Add( S );
  370. end;
  371.  
  372. function TAString.CharPosition( const StartingFrom: longint;
  373.                                 const CharToFind: Char): longint;
  374. var
  375.   StartP: PChar;
  376.   P: PChar;
  377.   EndP: PChar;
  378.   C: Char;
  379. begin
  380.   CheckValid( self );
  381.   Result:= -1;
  382.   if not ValidIndex( StartingFrom ) then
  383.     exit;
  384.   StartP:= _S + StartingFrom;
  385.   EndP:= _S + Length;
  386.   P:= StartP;
  387.  
  388.   while P < EndP do
  389.   begin
  390.     C:= P^;
  391.     if C = CharToFind then
  392.     begin
  393.       Result:= PCharDiff( p, _S );
  394.       break;
  395.     end;
  396.     inc( P );
  397.   end;
  398. end;
  399.  
  400. procedure TAString.Delete( const StartingFrom: longint;
  401.                            const LengthToDelete: longint );
  402. var
  403.   StartP: PChar;
  404.   EndP: PChar;
  405.   SizeToCopy: longint;
  406. begin
  407.   if not ValidIndex( StartingFrom ) then
  408.     exit;
  409.   if LengthToDelete = 0 then
  410.     exit;
  411.  
  412.   StartP:= _S + StartingFrom;
  413.   if StartingFrom + LengthToDelete >= Length then
  414.   begin
  415.     SetLength( StartingFrom );
  416.     exit;
  417.   end;
  418.   EndP:= _S + StartingFrom + LengthToDelete;
  419.   SizeToCopy:= Length - ( StartingFrom + LengthToDelete );
  420.   MemCopy( EndP, StartP, SizeToCopy );
  421.   SetLength( Length - LengthToDelete );
  422. end;
  423.  
  424. function TAString.ValidIndex( const Index: longint ): boolean;
  425. begin
  426.   Result:= ( Index >= 0 ) and ( Index < Length );
  427. end;
  428.  
  429. function TAString.GetAsString: string;
  430. begin
  431.   CheckValid( self );
  432. {$ifdef os2}
  433.   Result:= StrPas( _S );
  434. {$else}
  435.   Result:= _S;
  436. {$endif}
  437. end;
  438.  
  439. procedure TAString.SetLength( NewLength: longint );
  440. begin
  441.   CheckValid( self );
  442.   if NewLength < 0 then
  443.     exit;
  444.   CheckSize( NewLength );
  445.   _Length:= NewLength;
  446.   _S[ _Length ]:= #0;
  447.  
  448. end;
  449.  
  450. procedure TAString.ReadLn( var TheFile: TextFile );
  451. Var
  452.   C: Char;
  453.   FoundCR: boolean;
  454. Begin
  455.   CheckValid( self );
  456.   Clear;
  457.   FoundCR:= false;
  458.   while not eof( TheFile ) do
  459.   begin
  460.     Read( TheFile, C );
  461.     if ( C = #10 ) then
  462.     begin
  463.       if FoundCR then
  464.         exit; // reached end of line
  465.     end
  466.     else
  467.     begin
  468.       if FoundCR then
  469.         // last CR was not part of CR/LF so add to string
  470.         Add( #13 );
  471.     end;
  472.     FoundCR:= ( C = #13 );
  473.     if not FoundCR then // don't handle 13's till later
  474.     begin
  475.       Add( C );
  476.     end;
  477.   end;
  478.  
  479.   if FoundCR then
  480.     // CR was last char of file, but no LF so add to string
  481.     Add( #13 );
  482.  
  483. end;
  484.  
  485. procedure TAString.WriteLn( var TheFile: TextFile );
  486. var
  487.   P: PChar;
  488.   EndP: PChar;
  489.   C: Char;
  490. begin
  491.   CheckValid( self );
  492.  
  493.   P:= _S;
  494.   EndP:= _S + Length;
  495.  
  496.   while P < EndP do
  497.   begin
  498.     C:= P^;
  499.     Write( TheFile, C );
  500.     inc( P );
  501.   end;
  502.   Write( TheFile, #13 );
  503.   Write( TheFile, #10 );
  504. end;
  505.  
  506. function TAString.GetChar( Index: longint ): Char;
  507. begin
  508.   CheckValid( self );
  509.   CheckIndex( Index );
  510.   Result:= _S[ Index ];
  511. end;
  512.  
  513. procedure TAString.SetChar( Index: longint;
  514.                             const Value: Char );
  515. begin
  516.   CheckValid( self );
  517.   CheckIndex( Index );
  518.   _S[ Index ]:= Value;
  519. end;
  520.  
  521. procedure TAString.CheckIndex( const Index: longint );
  522. begin
  523.   if not ValidIndex( Index ) then
  524.     raise EAStringIndexError( 'Index '
  525.                               + IntToStr( Index )
  526.                               + ' is not in valid range ( 0 - '
  527.                               + IntToStr( Length - 1 )
  528.                               + ') for string' );
  529.  
  530. end;
  531.  
  532. procedure TAString.ParseKeyValuePair( KeyName: TAString;
  533.                                       KeyValue: TAString;
  534.                                       Seperator: Char );
  535. var
  536.   Position: longint;
  537. begin
  538.   Position:= 0;
  539.   ExtractNextValue( Position, KeyName, Seperator );
  540.   GetRightFrom( Position, KeyValue );
  541. end;
  542.  
  543.  
  544. procedure TAString.GetLeft( const Count: longint;
  545.                             Dest: TAString);
  546. begin
  547.   Dest.Clear;
  548.   if Count >= Length then
  549.     Dest.Assign( self )
  550.   else if Count > 0 then
  551.     Dest.AddData( _S, Count );
  552. end;
  553.  
  554. procedure TAString.GetRight( const Count: longint;
  555.                              Dest: TAString);
  556. begin
  557.   Dest.Clear;
  558.   if Count >= Length then
  559.     Dest.Assign( self )
  560.   else if Count > 0 then
  561.     Dest.AddData( _S + Length - Count - 1, Count );
  562. end;
  563.  
  564. procedure TAString.GetRightFrom( const StartingFrom: longint;
  565.                                  Dest: TAString);
  566. begin
  567.   Dest.Clear;
  568.   if StartingFrom <= 0  then
  569.     Dest.Assign( self )
  570.   else if StartingFrom < Length then
  571.     Dest.AddData( _S + StartingFrom, Length - StartingFrom );
  572. end;
  573.  
  574. function TAString.SameAs(S: String): boolean;
  575. begin
  576. {$ifdef os2}
  577.   if Length > 255 then
  578.   begin
  579.     Result:= false;
  580.     exit;
  581.   end;
  582.   Result:= StrIComp( _S, Addr( S ) + 1 ) = 0;
  583. {$else}
  584.   Result:= StrIComp( _S, PChar( S ) ) = 0;
  585. {$endif}
  586.  
  587. end;
  588.  
  589. function TAString.GetIsEmpty: boolean;
  590. begin
  591.   Result:= Length = 0;
  592. end;
  593.  
  594. procedure TAString.Trim;
  595. begin
  596.   TrimChar( #32 );
  597. end;
  598.  
  599. Initialization
  600. End.
  601.