source: branches/2.20_branch/Library/ACLString.pas@ 461

Last change on this file since 461 was 203, checked in by RBRi, 18 years ago

use char util

  • Property svn:eol-style set to native
File size: 19.1 KB
Line 
1Unit ACLString;
2
3Interface
4
5uses
6{$Ifdef os2}
7 BseDos,
8{$endif}
9 SysUtils;
10
11{
12 AString: A length-safe string class. Internally maintains
13 a length as well as a zero terminator, so very fast for concatenation
14
15 Attributes:
16 - zero-based indexing
17 - maintains zero terminator for passing as (readonly) c-style string (AsPChar)
18 - fast for concatenation
19 - no inherent length limit
20
21 You can write to it as pchar by calling SetMaxLength, using AsPChar,
22 then calling UpdateLength to set the length based on the term.
23
24 For Sibyl it is clumsier but faster for concatenating
25 than AnsiString and I trust it more...
26
27 For extra safety it explicitly checks that this is a valid instance of
28 AString on every method call (using an internal magic number)
29 Note :- because of the exception handling this is probably quite slow.
30
31 You can also call the global procedure
32 CheckAllAStringsDestroyed
33 at the end of the program to make sure there are no memory leaks
34 due to AStrings not being destroyed.
35
36 V1.2 28/6/00
37 Added ReadLn and WriteLn methods (for text files)
38 Added Character index property
39 V1.1 27/6/00
40 Added:
41 Delete - delete a seciton of string
42 Assign methods
43 AsString property
44 CharPosition function
45 ExtractNextValue method
46 This method, unlike my original string version, does not
47 alter the main string. Rather it takes and increments
48 a starting position.
49
50 V1.0
51 Completed basic functionality
52 Used in NewView for decoding help topics. Fast!
53}
54
55type
56 EAStringError = class( Exception );
57 EAStringIndexError = class( EAStringError );
58
59 TAString = class
60 private
61 function GetIsEmpty: boolean;
62 protected
63 _S: PChar;
64 _Length: longint;
65 _MagicNumber: longword;
66 procedure CheckSize( const NeededLength: longint );
67 procedure AddData( const Data: pointer;
68 const DataLength: longint );
69
70 Procedure InsertData( const Data: pointer;
71 const InsertPoint: longint;
72 const DataLength: longint );
73
74 procedure Initialise;
75
76 function ValidIndex( const Index: longint ): boolean;
77 procedure CheckIndex( const Index: longint );
78 function GetAsString: string;
79
80 procedure SetLength( NewLength: longint );
81
82 function GetChar( Index: longint ): Char;
83 procedure SetChar( Index: longint;
84 const Value: Char );
85
86 public
87
88 constructor Create;
89 constructor CreateFrom( const S: String );
90 constructor CreateFromPChar( const S: PChar );
91 constructor CreateFromPCharLen( const S: PChar; const Len: longint );
92 constructor CreateCopy( const S: TAString );
93
94 // Create a AString from the given PChar and
95 // dispose of the PChar. Useful for using when you can only
96 // get a PChar as a newly allocated string (e.g TMemo.Lines.GetText)
97 constructor CreateFromPCharWithDispose( const S: PChar );
98
99 destructor Destroy; override;
100
101 // Modifications
102 procedure Assign( const S: TAString );
103 procedure AssignString( const S: string );
104 procedure AssignPChar( const S: PChar );
105 procedure AssignPCharLen( const S: PChar; const Len: longint );
106
107 procedure Add( const S: TAString );
108 procedure AddString( const S: string );
109 procedure AddPChar( const S: PChar );
110 procedure AddPCharLen( const S: PChar; const Len: longint );
111 procedure AddChar( const C: char );
112
113 Procedure Insert( const InsertPoint: longword;
114 const S: TAString );
115 Procedure InsertString( const InsertPoint: longword;
116 const S: string );
117
118 procedure Trim;
119 procedure TrimChar( CharToTrim: Char );
120 procedure Delete( const StartingFrom: longint;
121 const LengthToDelete: longint );
122 procedure Clear;
123
124 // Properties
125 property AsPChar: PChar read _S;
126 property AsString: string read GetAsString;
127 property Character[ Index: longint ]: Char read GetChar write SetChar; default;
128 property Length: longint read _Length write SetLength;
129 property IsEmpty: boolean read GetIsEmpty;
130
131 // updates length based on terminator, use after writing to as pchar
132 Procedure UpdateLength;
133
134 // Queries
135 // returns -1 if not found
136 function CharPosition( const StartingFrom: longint;
137 const CharToFind: Char ): longint;
138
139 function SameAs( const S: String ): boolean;
140
141 // returns true if starts with S
142 function StartsWith( const S: String ): boolean;
143
144 // Extract the next value seperated by seperator
145 // starting at StartingFrom (zero based index!)
146 procedure ExtractNextValue( Var StartingFrom: longint;
147 ExtractTo: TAString;
148 const Seperator: Char );
149 procedure GetRightFrom( const StartingFrom: longint;
150 Dest: TAString );
151 procedure GetLeft( const Count: longint;
152 Dest: TAString );
153 procedure GetRight( const Count: longint;
154 Dest: TAString );
155 procedure ParseKeyValuePair( KeyName: TAString;
156 KeyValue: TAString;
157 Seperator: Char );
158
159 // Make sure the string can contain at least MaxLength chars
160 // Use before passing AsPChar to a function that writes a PChar
161 procedure SetMaxLength( MaxLength: longint );
162
163 // Read a line from the given file. Line must end
164 // with #13 #10. ( Single #13 or #10 not recognised )
165 procedure ReadLn( Var TheFile: TextFile );
166 procedure WriteLn( Var TheFile: TextFile );
167
168{$Ifdef os2}
169 // Read a line from the given file handle
170 function ReadParagraph( F: HFile ): boolean;
171{$endif}
172 end;
173
174// call this to be sure all AStrings have been destroyed.
175procedure CheckAllAStringsDestroyed;
176
177Implementation
178
179uses
180{$Ifdef os2}
181 OS2Def,
182 ACLFileIOUtility,
183{$endif}
184 ACLUtility,
185 CharUtilsUnit;
186
187const
188 GlobalAStringCreatedCount: longint = 0;
189 GlobalAStringDestroyedCount: longint = 0;
190
191const
192 MagicConstant = $cabba9e;
193
194procedure CheckAllAStringsDestroyed;
195begin
196 if GlobalAStringCreatedCount > GlobalAStringDestroyedCount then
197 raise Exception.Create( 'Not all AStrings have been destroyed ('
198 + IntToStr( GlobalAStringCreatedCount )
199 + ' created, '
200 + IntToStr( GlobalAStringDestroyedCount )
201 + ' destroyed). Possible memory leak.' );
202end;
203
204procedure CheckValid( const S: TAString );
205var
206 IsValid: boolean;
207begin
208 try
209 IsValid:= S._MagicNumber = MagicConstant;
210 except
211 IsValid:= false;
212 end;
213 if not IsValid then
214 raise Exception.Create( 'Reference to invalid AString' );
215end;
216
217constructor TAString.Create;
218begin
219 inherited Create;
220 Initialise;
221end;
222
223procedure TAString.Initialise;
224begin
225 inc( GlobalAStringCreatedCount );
226 _S:= StrAlloc( 16 );
227 _MagicNumber:= MagicConstant;
228 Clear;
229end;
230
231constructor TAString.CreateFrom( const S: String );
232begin
233 Initialise;
234 AssignString( S );
235end;
236
237constructor TAString.CreateFromPChar( const S: PChar );
238begin
239 Initialise;
240 AssignPChar( S );
241end;
242
243constructor TAString.CreateFromPCharLen( const S: PChar; const Len: longint );
244begin
245 Initialise;
246 AssignPCharLen( S, Len );
247end;
248
249constructor TAString.CreateFromPCharWithDispose( const S: PChar );
250begin
251 Initialise;
252 AddPChar( S );
253 StrDispose( S );
254end;
255
256constructor TAString.CreateCopy( const S: TAString );
257begin
258 Initialise;
259 Assign( S );
260end;
261
262destructor TAString.Destroy;
263begin
264 inc( GlobalAStringDestroyedCount );
265 StrDispose( _S );
266 _MagicNumber:= 0;
267 inherited Destroy;
268end;
269
270procedure TAString.CheckSize( const NeededLength: longint );
271var
272 temp: PChar;
273 NewBufferSize: longint;
274 CurrentBufferSize: longint;
275begin
276 CurrentBufferSize:= StrBufSize( _S );
277 if NeededLength + 1 > CurrentBufferSize then
278 begin
279 // allocate new buffer, double the size...
280 NewBufferSize:= CurrentBufferSize * 2;
281 // or if that's not enough...
282 if NewBufferSize < NeededLength + 1 then
283 // double what we are going to need
284 NewBufferSize:= NeededLength * 2;
285
286 temp:= StrAlloc( NewBufferSize );
287
288 MemCopy( _S,
289 Temp,
290 _Length + 1 );
291
292 StrDispose( _S );
293 _S:= temp;
294 end;
295end;
296
297procedure TAString.Clear;
298begin
299 CheckValid( self );
300 _Length:= 0;
301 _S[ 0 ]:= #0;
302end;
303
304procedure TAString.AddData( const Data: pointer;
305 const DataLength: longint );
306begin
307 if DataLength = 0 then
308 exit;
309 CheckSize( _Length + DataLength );
310 MemCopy( Data, _S + _Length, DataLength );
311 inc( _Length, DataLength );
312 _S[ _Length ]:= #0;
313end;
314
315Procedure TAString.InsertData( const Data: pointer;
316 const InsertPoint: longint;
317 const DataLength: longint );
318begin
319 // we can also insert at the very end... which is not a valid index
320 if InsertPoint <> _Length then
321 if not ValidIndex( InsertPoint ) then
322 exit;
323
324 CheckSize( _Length + DataLength );
325
326 // shift the existing text, past the insert point,
327 // up to make room
328 MemCopy( _S + InsertPoint,
329 _S + InsertPoint + DataLength,
330 _Length - InsertPoint );
331
332 MemCopy( Data,
333 _S + InsertPoint,
334 DataLength );
335
336 inc( _Length, DataLength );
337
338 // zero terminate
339 _S[ _Length ] := #0;
340
341end;
342
343procedure TAString.Add( const S: TAString );
344begin
345 CheckValid( self );
346 CheckValid( S );
347 AddData( S._S, S.Length );
348end;
349
350procedure TAString.AddPChar( const S: PChar );
351begin
352 CheckValid( self );
353 AddData( S, StrLen( S ) );
354end;
355
356procedure TAString.AddString( const S: string );
357begin
358 CheckValid( self );
359{$ifdef os2}
360 AddData( Addr( S ) + 1, System.Length( S ) );
361{$else}
362 AddData( PChar( S ), System.Length( S ) );
363{$endif}
364end;
365
366procedure TAString.AddChar( const C: char );
367begin
368 CheckValid( self );
369 AddData( Addr( C ), 1 );
370end;
371
372procedure TAString.TrimChar( CharToTrim: Char );
373var
374 StartP: PChar;
375 EndP: PChar;
376 C: Char;
377begin
378 CheckValid( self );
379 if _Length = 0 then
380 exit;
381 StartP:= _S;
382 EndP:= _S + Length;
383
384 while StartP < EndP do
385 begin
386 C:= StartP^;
387 if C <> CharToTrim then
388 break;
389 inc( StartP );
390 end;
391 // StartP now points to first non-space char
392
393 while EndP > StartP do
394 begin
395 dec( EndP );
396 C:= EndP^;
397 if C <> CharToTrim then
398 begin
399 inc( EndP );
400 break;
401 end;
402 end;
403 // EndP now points to one byte past last non-space char
404
405 _Length:= PCharPointerDiff( EndP, StartP );
406
407 if _Length > 0 then
408 if StartP > _S then
409 MemCopy( StartP, _S, _Length );
410
411 _S[ _Length ]:= #0;
412
413end;
414
415procedure TAString.ExtractNextValue( Var StartingFrom: longint;
416 ExtractTo: TAString;
417 const Seperator: Char );
418var
419 NextSeperatorPosition: longint;
420begin
421 CheckValid( self );
422 CheckValid( ExtractTo );
423
424 ExtractTo.Clear;
425 if StartingFrom >= Length then
426 exit;
427 NextSeperatorPosition:= CharPosition( StartingFrom,
428 Seperator );
429 if NextSeperatorPosition > -1 then
430 begin
431 ExtractTo.AddData( _S + StartingFrom,
432 NextSeperatorPosition - StartingFrom );
433 StartingFrom:= NextSeperatorPosition + 1;
434 end
435 else
436 begin
437 ExtractTo.AddData( _S + StartingFrom,
438 Length - StartingFrom );
439 StartingFrom:= Length;
440 end;
441 ExtractTo.Trim;
442
443end;
444
445procedure TAString.Assign( const S: TAString );
446begin
447 Clear;
448 Add( S );
449end;
450
451procedure TAString.AssignPChar( const S: PChar);
452begin
453 Clear;
454 AddPChar( S );
455end;
456
457procedure TAString.AssignPCharLen( const S: PChar; const Len: longint );
458begin
459 Clear;
460 AddPCharLen( S, Len );
461end;
462
463procedure TAString.AssignString( const S: string );
464begin
465 Clear;
466 AddString( S );
467end;
468
469function TAString.CharPosition( const StartingFrom: longint;
470 const CharToFind: Char ): longint;
471var
472 StartP: PChar;
473 P: PChar;
474 EndP: PChar;
475 C: Char;
476begin
477 CheckValid( self );
478 Result:= -1;
479 if not ValidIndex( StartingFrom ) then
480 exit;
481 StartP:= _S + StartingFrom;
482 EndP:= _S + Length;
483 P:= StartP;
484
485 while P < EndP do
486 begin
487 C:= P^;
488 if C = CharToFind then
489 begin
490 Result:= PCharPointerDiff( p, _S );
491 break;
492 end;
493 inc( P );
494 end;
495end;
496
497procedure TAString.Delete( const StartingFrom: longint;
498 const LengthToDelete: longint );
499var
500 StartP: PChar;
501 EndP: PChar;
502 SizeToCopy: longint;
503begin
504 if not ValidIndex( StartingFrom ) then
505 exit;
506 if LengthToDelete = 0 then
507 exit;
508
509 StartP:= _S + StartingFrom;
510 if StartingFrom + LengthToDelete >= Length then
511 begin
512 SetLength( StartingFrom );
513 exit;
514 end;
515 EndP:= _S + StartingFrom + LengthToDelete;
516 SizeToCopy:= Length - ( StartingFrom + LengthToDelete );
517 MemCopy( EndP, StartP, SizeToCopy );
518 SetLength( Length - LengthToDelete );
519end;
520
521function TAString.ValidIndex( const Index: longint ): boolean;
522begin
523 Result:= ( Index >= 0 ) and ( Index < Length );
524end;
525
526function TAString.GetAsString: string;
527begin
528 CheckValid( self );
529{$ifdef os2}
530 Result:= StrPas( _S );
531{$else}
532 Result:= _S;
533{$endif}
534end;
535
536procedure TAString.SetLength( NewLength: longint );
537begin
538 CheckValid( self );
539 if NewLength < 0 then
540 exit;
541 CheckSize( NewLength );
542 _Length:= NewLength;
543 _S[ _Length ]:= #0;
544
545end;
546
547procedure TAString.ReadLn( var TheFile: TextFile );
548Var
549 C: Char;
550 FoundCR: boolean;
551Begin
552 CheckValid( self );
553 Clear;
554 FoundCR:= false;
555 while not eof( TheFile ) do
556 begin
557 Read( TheFile, C );
558 if ( C = #10 ) then
559 begin
560 if FoundCR then
561 exit; // reached end of line
562 end
563 else
564 begin
565 if FoundCR then
566 // last CR was not part of CR/LF so add to string
567 AddString( #13 );
568 end;
569 FoundCR:= ( C = #13 );
570 if not FoundCR then // don't handle 13's till later
571 begin
572 AddString( C );
573 end;
574 end;
575
576 if FoundCR then
577 // CR was last char of file, but no LF so add to string
578 AddString( #13 );
579
580end;
581
582procedure TAString.WriteLn( var TheFile: TextFile );
583var
584 P: PChar;
585 EndP: PChar;
586 C: Char;
587begin
588 CheckValid( self );
589
590 P:= _S;
591 EndP:= _S + Length;
592
593 while P < EndP do
594 begin
595 C:= P^;
596 Write( TheFile, C );
597 inc( P );
598 end;
599 Write( TheFile, #13 );
600 Write( TheFile, #10 );
601end;
602
603function TAString.GetChar( Index: longint ): Char;
604begin
605 CheckValid( self );
606 CheckIndex( Index );
607 Result:= _S[ Index ];
608end;
609
610procedure TAString.SetChar( Index: longint;
611 const Value: Char );
612begin
613 CheckValid( self );
614 CheckIndex( Index );
615 _S[ Index ]:= Value;
616end;
617
618procedure TAString.CheckIndex( const Index: longint );
619begin
620 if not ValidIndex( Index ) then
621 raise EAStringIndexError( 'Index '
622 + IntToStr( Index )
623 + ' is not in valid range ( 0 - '
624 + IntToStr( Length - 1 )
625 + ') for string' );
626
627end;
628
629procedure TAString.ParseKeyValuePair( KeyName: TAString;
630 KeyValue: TAString;
631 Seperator: Char );
632var
633 Position: longint;
634begin
635 CheckValid( self );
636 Position:= 0;
637 ExtractNextValue( Position, KeyName, Seperator );
638 GetRightFrom( Position, KeyValue );
639end;
640
641
642procedure TAString.GetLeft( const Count: longint;
643 Dest: TAString );
644begin
645 CheckValid( self );
646 Dest.Clear;
647 if Count >= Length then
648 Dest.Assign( self )
649 else if Count > 0 then
650 Dest.AddData( _S, Count );
651end;
652
653procedure TAString.GetRight( const Count: longint;
654 Dest: TAString );
655begin
656 CheckValid( self );
657 Dest.Clear;
658 if Count >= Length then
659 Dest.Assign( self )
660 else if Count > 0 then
661 Dest.AddData( _S + Length - Count - 1, Count );
662end;
663
664procedure TAString.GetRightFrom( const StartingFrom: longint;
665 Dest: TAString );
666begin
667 CheckValid( self );
668 Dest.Clear;
669 if StartingFrom <= 0 then
670 Dest.Assign( self )
671 else if StartingFrom < Length then
672 Dest.AddData( _S + StartingFrom, Length - StartingFrom );
673end;
674
675{$ifdef os2}
676function TAString.SameAs( const S: String ): boolean;
677var
678 DataCompareResult: integer;
679begin
680 CheckValid( self );
681 if Length <> System.Length( S ) then
682 begin
683 // different lengths -> strings are different
684 result := false;
685 exit;
686 end;
687 // length is same, so compare data, case insensitive
688 DataCompareResult := StrLIComp( _S,
689 Addr( S ) + 1,
690 Length );
691 Result := DataCompareResult = 0;
692end;
693
694// returns true if starts with S
695function TAString.StartsWith( const S: String ): boolean;
696var
697 DataCompareResult: integer;
698begin
699 CheckValid( self );
700 if System.Length( S ) > Length then
701 begin
702 // S is longer than us; we can't start with it
703 result := false;
704 exit;
705 end;
706 // compare data, case insensitive
707 DataCompareResult := StrLIComp( _S,
708 Addr( S ) + 1,
709 System.Length( S ) );
710 Result := DataCompareResult = 0;
711end;
712
713{$else} // win32
714
715// returns true if starts with S
716function TAString.StartsWith( const S: String ): boolean;
717var
718 DataCompareResult: integer;
719begin
720 CheckValid( self );
721 if System.Length( S ) > Length then
722 begin
723 // S is longer than us; we can't start with it
724 result := false;
725 exit;
726 end;
727 // compare data, case insensitive
728 DataCompareResult := StrLIComp( _S,
729 PChar(s),
730 System.Length( S ) );
731 Result := DataCompareResult = 0;
732end;
733
734function TAString.SameAs( const S: String ): boolean;
735begin
736 CheckValid( self );
737 Result:= StrIComp( _S, PChar( S ) ) = 0;
738end;
739{$endif}
740
741function TAString.GetIsEmpty: boolean;
742begin
743 CheckValid( self );
744 Result:= Length = 0;
745end;
746
747procedure TAString.Trim;
748begin
749 CheckValid( self );
750 TrimChar( #32 );
751end;
752
753procedure TAString.SetMaxLength( MaxLength: longint );
754begin
755 CheckValid( self );
756 CheckSize( MaxLength );
757end;
758
759procedure TAString.AddPCharLen( const S: PChar; const Len: longint );
760begin
761 CheckValid( self );
762 AddData( S, Len );
763end;
764
765Procedure TAString.Insert( const InsertPoint: longword;
766 const S: TAString );
767begin
768 CheckValid( self );
769 InsertData( S._S,
770 InsertPoint,
771 S._Length );
772end;
773
774Procedure TAString.InsertString( const InsertPoint: longword;
775 const S: string );
776begin
777 CheckValid( self );
778{$ifdef os2}
779 InsertData( Addr( S ) + 1,
780 InsertPoint,
781 System.Length( S ) );
782{$else}
783 InsertData( PChar( S ),
784 InsertPoint,
785 System.Length( S ) );
786{$endif}
787end;
788
789{$Ifdef os2}
790function TAString.ReadParagraph( F: HFile ): boolean;
791var
792 C: Char;
793 NewFilePtr: ULONG;
794begin
795 Clear;
796
797 Result:= MyRead( F, Addr( C ), 1 );
798 while ( C <> #13 )
799 and Result do
800 begin
801 AddChar( C );
802 Result:= MyRead( F, Addr( C ), 1 );
803 end;
804
805 if not Result then
806 exit;
807
808 // skip #10 if found
809 Result:= MyRead( F, Addr( C ), 1 );
810 if Result then
811 if C <> #10 then
812 DosSetFilePtr( F, -1, FILE_CURRENT, NewFilePtr );
813end;
814{$endif}
815
816// updates length based on terminator, use after writing to as pchar
817Procedure TAString.UpdateLength;
818begin
819 SetLength( StrLen( _S ) );
820end;
821
822Initialization
823End.
Note: See TracBrowser for help on using the repository browser.