Initial revision
[AROS-Contrib.git] / development / compilers / freepascal / rtl / inc / astrings.inc
bloba3339239717b50f72499db4a16a190c6d121831e
2     $Id$
3     This file is part of the Free Pascal run time library.
4     Copyright (c) 1999-2000 by Michael Van Canneyt,
5     member of the Free Pascal development team.
7     This file implements AnsiStrings for FPC
9     See the file COPYING.FPC, included in this distribution,
10     for details about the copyright.
12     This program is distributed in the hope that it will be useful,
13     but WITHOUT ANY WARRANTY; without even the implied warranty of
14     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  **********************************************************************}
18 { This will release some functions for special shortstring support }
19 { define EXTRAANSISHORT}
22   This file contains the implementation of the AnsiString type,
23   and all things that are needed for it.
24   AnsiString is defined as a 'silent' pchar :
25   a pchar that points to :
27   @-12 : Longint for maximum size;
28   @-8  : Longint for size;
29   @-4  : Longint for reference count;
30   @    : String + Terminating #0;
31   Pchar(Ansistring) is a valid typecast.
32   So AS[i] is converted to the address @AS+i-1.
34   Constants should be assigned a reference count of -1
35   Meaning that they can't be disposed of.
38 Type
39   PAnsiRec = ^TAnsiRec;
40   TAnsiRec = Packed Record
41     Maxlen,
42     len,
43     ref   : Longint;
44     First : Char;
45   end;
47 Const
48   AnsiRecLen = SizeOf(TAnsiRec);
49   FirstOff   = SizeOf(TAnsiRec)-1;
52 {****************************************************************************
53                     Internal functions, not in interface.
54 ****************************************************************************}
56 {$ifdef AnsiStrDebug}
57 Procedure DumpAnsiRec(S : Pointer);
58 begin
59   If S=Nil then
60     Writeln ('String is nil')
61   Else
62     Begin
63       With PAnsiRec(S-Firstoff)^ do
64        begin
65          Write   ('(Maxlen: ',maxlen);
66          Write   (' Len:',len);
67          Writeln (' Ref: ',ref,')');
68        end;
69     end;
70 end;
71 {$endif}
74 Function NewAnsiString(Len : Longint) : Pointer;
76   Allocate a new AnsiString on the heap.
77   initialize it to zero length and reference count 1.
79 Var
80   P : Pointer;
81 begin
82   { Also add +1 for a terminating zero }
83   GetMem(P,Len+AnsiRecLen);
84   If P<>Nil then
85    begin
86      PAnsiRec(P)^.Maxlen:=Len;    { Maximal length }
87      PAnsiRec(P)^.Len:=0;         { Initial length }
88      PAnsiRec(P)^.Ref:=1;         { Set reference count }
89      PAnsiRec(P)^.First:=#0;      { Terminating #0 }
90      P:=P+FirstOff;               { Points to string now }
91    end;
92   NewAnsiString:=P;
93 end;
96 Procedure DisposeAnsiString(Var S : Pointer);
98   Deallocates a AnsiString From the heap.
100 begin
101   If S=Nil then
102     exit;
103   Dec (Longint(S),FirstOff);
104   FreeMem (S);
105   S:=Nil;
106 end;
109 Procedure AnsiStr_Decr_Ref (Var S : Pointer);[Public,Alias:'FPC_ANSISTR_DECR_REF'];
111   Decreases the ReferenceCount of a non constant ansistring;
112   If the reference count is zero, deallocate the string;
114 Type
115   plongint = ^longint;
117   l : plongint;
118 Begin
119   { Zero string }
120   If S=Nil then exit;
121   { check for constant strings ...}
122   l:=@PANSIREC(S-FirstOff)^.Ref;
123   If l^<0 then exit;
124   Dec(l^);
125   If l^=0 then
126     { Ref count dropped to zero }
127     DisposeAnsiString (S);        { Remove...}
128   { this pointer is not valid anymore, so set it to zero }
129   S:=nil;
130 end;
133 Procedure AnsiStr_Incr_Ref (Var S : Pointer);[Public,Alias:'FPC_ANSISTR_INCR_REF'];
134 Begin
135   If S=Nil then
136     exit;
137   { Let's be paranoid : Constant string ??}
138   If PAnsiRec(S-FirstOff)^.Ref<0 then exit;
139   Inc(PAnsiRec(S-FirstOff)^.Ref);
140 end;
143 Procedure AnsiStr_Assign (Var S1 : Pointer;S2 : Pointer);[Public,Alias:'FPC_ANSISTR_ASSIGN'];
145   Assigns S2 to S1 (S1:=S2), taking in account reference counts.
147 begin
148   If S2<>nil then
149     If PAnsiRec(S2-FirstOff)^.Ref>0 then
150       Inc(PAnsiRec(S2-FirstOff)^.ref);
151   { Decrease the reference count on the old S1 }
152   ansistr_decr_ref (S1);
153   { And finally, have S1 pointing to S2 (or its copy) }
154   S1:=S2;
155 end;
158 Procedure AnsiStr_Concat (S1,S2 : Pointer;var S3 : Pointer);[Public, alias: 'FPC_ANSISTR_CONCAT'];
160   Concatenates 2 AnsiStrings : S1+S2.
161   Result Goes to S3;
164   Size,Location : Longint;
165 begin
166 { create new result }
167   if S3<>nil then
168     AnsiStr_Decr_Ref(S3);
169 { only assign if s1 or s2 is empty }
170   if (S1=Nil) then
171     AnsiStr_Assign(S3,S2)
172   else
173     if (S2=Nil) then
174       AnsiStr_Assign(S3,S1)
175   else
176     begin
177        Size:=PAnsiRec(S2-FirstOff)^.Len;
178        Location:=Length(AnsiString(S1));
179        SetLength (AnsiString(S3),Size+Location);
180        Move (S1^,S3^,Location);
181        Move (S2^,(S3+location)^,Size+1);
182     end;
183 end;
186 {$ifdef EXTRAANSISHORT}
187 Procedure AnsiStr_ShortStr_Concat (Var S1: AnsiString; Var S2 : ShortString);
189   Concatenates a Ansi with a short string; : S2 + S2
192   Size,Location : Longint;
193 begin
194   Size:=Length(S2);
195   Location:=Length(S1);
196   If Size=0 then
197     exit;
198   { Setlength takes case of uniqueness
199     and alllocated memory. We need to use length,
200     to take into account possibility of S1=Nil }
201   SetLength (S1,Size+Length(S1));
202   Move (S2[1],Pointer(Pointer(S1)+Location)^,Size);
203   PByte( Pointer(S1)+length(S1) )^:=0; { Terminating Zero }
204 end;
205 {$endif EXTRAANSISHORT}
208 Procedure AnsiStr_To_ShortStr (Var S1 : ShortString;S2 : Pointer);[Public, alias: 'FPC_ANSISTR_TO_SHORTSTR'];
210   Converts a AnsiString to a ShortString;
213   Size : Longint;
214 begin
215   if S2=nil then
216    S1:=''
217   else
218    begin
219      Size:=PAnsiRec(S2-FirstOff)^.Len;
220      If Size>high(S1) then
221       Size:=high(S1);
222      Move (S2^,S1[1],Size);
223      byte(S1[0]):=Size;
224    end;
225 end;
228 Procedure ShortStr_To_AnsiStr (Var S1 : Pointer; Const S2 : ShortString);[Public, alias: 'FPC_SHORTSTR_TO_ANSISTR'];
230   Converts a ShortString to a AnsiString;
233   Size : Longint;
234 begin
235   Size:=Length(S2);
236   Setlength (AnsiString(S1),Size);
237   if Size>0 then
238    begin
239      Move (S2[1],Pointer(S1)^,Size);
240      { Terminating Zero }
241      PByte(Pointer(S1)+Size)^:=0;
242    end;
243 end;
246 Procedure Char_To_AnsiStr(var S1 : Pointer; c : Char);[Public, alias: 'FPC_CHAR_TO_ANSISTR'];
248   Converts a ShortString to a AnsiString;
250 begin
251   Setlength (AnsiString(S1),1);
252   PByte(Pointer(S1))^:=byte(c);
253   { Terminating Zero }
254   PByte(Pointer(S1)+1)^:=0;
255 end;
258 Procedure PChar_To_AnsiStr(var a : ansistring;p : pchar);[Public,Alias : 'FPC_PCHAR_TO_ANSISTR'];
260   L : Longint;
261 begin
262   if pointer(a)<>nil then
263     begin
264        AnsiStr_Decr_Ref(Pointer(a));
265        pointer(a):=nil;
266     end;
267   if (not assigned(p)) or (p[0]=#0) Then
268     Pointer(a):=nil
269   else
270     begin
271       //!! Horribly inneficient, but I see no other way...
272       L:=1;
273       While P[l]<>#0 do
274         inc (l);
275       Pointer(a):=NewAnsistring(L);
276       SetLength(A,L);
277       Move (P[0],Pointer(A)^,L)
278     end;
279 end;
282 Procedure CharArray_To_AnsiStr(var a : ansistring;p : pchar;l:longint);[Public,Alias : 'FPC_CHARARRAY_TO_ANSISTR'];
284   i  : longint;
285   hp : pchar;
286 begin
287   if p[0]=#0 Then
288     Pointer(a):=nil
289   else
290     begin
291       { p[0] <> #0, checked above (JM) }
292       hp:=p+1;
293       i:=1;
294       while (i<l) and (hp^<>#0) do
295        begin
296          inc(hp);
297          inc(i);
298        end;
299       Pointer(a):=NewAnsistring(i);
300       SetLength(A,i);
301       Move (P[0],Pointer(A)^,i);
302     end;
303 end;
306 Function AnsiStr_Compare(S1,S2 : Pointer): Longint;[Public,Alias : 'FPC_ANSISTR_COMPARE'];
308   Compares 2 AnsiStrings;
309   The result is
310    <0 if S1<S2
311    0 if S1=S2
312    >0 if S1>S2
315   i,MaxI,Temp : Longint;
316 begin
317   i:=0;
318   Maxi:=Length(AnsiString(S1));
319   temp:=Length(AnsiString(S2));
320   If MaxI>Temp then
321    MaxI:=Temp;
322   Temp:=0;
323   While (i<MaxI) and (Temp=0) do
324    begin
325      Temp:= PByte(S1+I)^ - PByte(S2+i)^;
326      inc(i);
327    end;
328   if temp=0 then
329    temp:=Length(AnsiString(S1))-Length(AnsiString(S2));
330   AnsiStr_Compare:=Temp;
331 end;
334 Procedure AnsiStr_CheckZero(p : pointer);[Public,Alias : 'FPC_ANSISTR_CHECKZERO'];
335 begin
336   if p=nil then
337     HandleErrorFrame(201,get_frame);
338 end;
341 Procedure AnsiStr_CheckRange(len,index : longint);[Public,Alias : 'FPC_ANSISTR_RANGECHECK'];
342 begin
343   if (index>len) or (Index<1) then
344     HandleErrorFrame(201,get_frame);
345 end;
348 {$ifdef EXTRAANSISHORT}
349 Function AnsiStr_ShortStr_Compare (Var S1 : Pointer; Var S2 : ShortString): Longint;
351   Compares a AnsiString with a ShortString;
352   The result is
353    <0 if S1<S2
354    0 if S1=S2
355    >0 if S1>S2
358   i,MaxI,Temp : Longint;
359 begin
360   Temp:=0;
361   i:=0;
362   MaxI:=Length(AnsiString(S1));
363   if MaxI>byte(S2[0]) then
364     MaxI:=Byte(S2[0]);
365   While (i<MaxI) and (Temp=0) do
366    begin
367      Temp:= PByte(S1+I)^ - Byte(S2[i+1]);
368      inc(i);
369    end;
370   AnsiStr_ShortStr_Compare:=Temp;
371 end;
372 {$endif EXTRAANSISHORT}
375 {*****************************************************************************
376                      Public functions, In interface.
377 *****************************************************************************}
379 Function Length (Const S : AnsiString) : Longint;
381   Returns the length of an AnsiString.
382   Takes in acount that zero strings are NIL;
384 begin
385   If Pointer(S)=Nil then
386     Length:=0
387   else
388     Length:=PAnsiRec(Pointer(S)-FirstOff)^.Len;
389 end;
392 Procedure SetLength (Var S : AnsiString; l : Longint);
394   Sets The length of string S to L.
395   Makes sure S is unique, and contains enough room.
398   Temp : Pointer;
399   movelen: longint;
400 begin
401    if (l>0) then
402     begin
403       if Pointer(S)=nil then
404        begin
405          { Need a complete new string...}
406          Pointer(s):=NewAnsiString(l);
407        end
408       else
409        If (PAnsiRec(Pointer(S)-FirstOff)^.Maxlen < L) or
410           (PAnsiRec(Pointer(S)-FirstOff)^.Ref <> 1) then
411         begin
412           { Reallocation is needed... }
413           Temp:=Pointer(NewAnsiString(L));
414           if Length(S)>0 then
415             begin
416               if l < succ(length(s)) then
417                 movelen := l
418               { also move terminating null }
419               else movelen := succ(length(s));
420               Move(Pointer(S)^,Temp^,movelen);
421             end;
422           ansistr_decr_ref(Pointer(S));
423           Pointer(S):=Temp;
424        end;
425       { Force nil termination in case it gets shorter }
426       PByte(Pointer(S)+l)^:=0;
427       PAnsiRec(Pointer(S)-FirstOff)^.Len:=l;
428     end
429   else
430     begin
431       { Length=0 }
432       if Pointer(S)<>nil then
433        ansistr_decr_ref (Pointer(S));
434       Pointer(S):=Nil;
435     end;
436 end;
439 Procedure UniqueString(Var S : AnsiString); [Public,Alias : 'FPC_ANSISTR_UNIQUE'];
441   Make sure reference count of S is 1,
442   using copy-on-write semantics.
445   SNew : Pointer;
446 begin
447   If Pointer(S)=Nil then
448     exit;
449   if PAnsiRec(Pointer(S)-Firstoff)^.Ref<>1 then
450    begin
451      SNew:=NewAnsiString (PAnsiRec(Pointer(S)-FirstOff)^.len);
452      Move (Pointer(S)^,SNew^,PAnsiRec(Pointer(S)-FirstOff)^.len+1);
453      PAnsiRec(SNew-FirstOff)^.len:=PAnsiRec(Pointer(S)-FirstOff)^.len;
454      ansistr_decr_ref (Pointer(S));  { Thread safe }
455      Pointer(S):=SNew;
456    end;
457 end;
460 Function Copy (Const S : AnsiString; Index,Size : Longint) : AnsiString;
462   ResultAddress : Pointer;
463 begin
464   ResultAddress:=Nil;
465   dec(index);
466   if Index < 0 then
467     Index := 0;
468   { Check Size. Accounts for Zero-length S, the double check is needed because
469     Size can be maxint and will get <0 when adding index }
470   if (Size>Length(S)) or
471      (Index+Size>Length(S)) then
472    Size:=Length(S)-Index;
473   If Size>0 then
474    begin
475      If Index<0 Then
476       Index:=0;
477      ResultAddress:=Pointer(NewAnsiString (Size));
478      if ResultAddress<>Nil then
479       begin
480         Move (Pointer(Pointer(S)+index)^,ResultAddress^,Size);
481         PAnsiRec(ResultAddress-FirstOff)^.Len:=Size;
482         PByte(ResultAddress+Size)^:=0;
483       end;
484    end;
485   Pointer(Copy):=ResultAddress;
486 end;
489 Function Pos (Const Substr : AnsiString; Const Source : AnsiString) : Longint;
491   substrlen,
492   maxi,
493   i,j : longint;
494   e   : boolean;
495   S   : AnsiString;
496   se  : Pointer;
497 begin
498   i := 0;
499   j := 0;
500   substrlen:=Length(SubStr);
501   maxi:=length(source)-substrlen;
502   e:=(substrlen>0);
503   while (e) and (i <= maxi) do
504    begin
505      inc (i);
506      if Source[i]=SubStr[1] then
507       begin
508         S:=copy(Source,i,substrlen);
509         Se:=pointer(SubStr);
510         if AnsiStr_Compare(se,Pointer(S))=0 then
511          begin
512            j := i;
513            break;
514          end;
515       end;
516    end;
517   pos := j;
518 end;
521 { Faster version for a char alone. Must be implemented because   }
522 { pos(c: char; const s: shortstring) also exists, so otherwise   }
523 { using pos(char,pchar) will always call the shortstring version }
524 { (exact match for first argument), also with $h+ (JM)           }
525 Function Pos (c : Char; Const s : AnsiString) : Longint;
527   i: longint;
528 begin
529   for i:=1 to length(s) do
530    if s[i]=c then
531     begin
532       pos:=i;
533       exit;
534     end;
535   pos:=0;
536 end;
539 Function ValAnsiFloat(Const S : AnsiString; Var Code : ValSInt): ValReal; [public, alias:'FPC_VAL_REAL_ANSISTR'];
541   SS : String;
542 begin
543   AnsiStr_To_ShortStr(SS,Pointer(S));
544   ValAnsiFloat := ValFloat(SS,Code);
545 end;
548 Function ValAnsiUnsignedInt (Const S : AnsiString; Var Code : ValSInt): ValUInt; [public, alias:'FPC_VAL_UINT_ANSISTR'];
550   SS : ShortString;
551 begin
552   AnsiStr_To_ShortStr(SS,Pointer(S));
553   ValAnsiUnsignedInt := ValUnsignedInt(SS,Code);
554 end;
557 Function ValAnsiSignedInt (DestSize: longint; Const S : AnsiString; Var Code : ValSInt): ValSInt; [public, alias:'FPC_VAL_SINT_ANSISTR'];
559   SS : ShortString;
560 begin
561   AnsiStr_To_ShortStr (SS,Pointer(S));
562   ValAnsiSignedInt := ValSignedInt(DestSize,SS,Code);
563 end;
565 Function ValAnsiUnsignedint64 (Const S : AnsiString; Var Code : ValSInt): qword; [public,alias:'FPC_VAL_QWORD_ANSISTR'];
567   SS : ShortString;
568 begin
569   if length(S)>255 then
570     code:=256
571   else
572     begin
573        AnsiStr_To_ShortStr(SS,Pointer(S));
574        ValAnsiUnsignedInt64 := ValQWord(SS,Code);
575     end;
576 end;
579 Function ValAnsiSignedInt64 (DestSize: longint; Const S : AnsiString; Var Code : ValSInt): Int64; [public, alias:'FPC_VAL_INT64_ANSISTR'];
581   SS : ShortString;
582 begin
583   if Length(S)>255 then
584     Code:=0
585   else
586     begin
587        AnsiStr_To_ShortStr (SS,Pointer(S));
588        ValAnsiSignedInt64 := valInt64(SS,Code);
589     end;
590 end;
592 {$IfDef SUPPORT_FIXED}
593 Function ValAnsiFixed(Const S : AnsiString; Var Code : ValSint): ValReal; [public, alias:'FPC_VAL_FIXED_ANSISTR'];
595   SS : String;
596 begin
597   AnsiStr_To_ShortStr (SS,Pointer(S));
598   ValAnsiFixed := Fixed(ValFloat(SS,Code));
599 end;
600 {$EndIf SUPPORT_FIXED}
603 procedure AnsiStr_Float(d : ValReal;len,fr,rt : longint;var s : ansistring);[public,alias:'FPC_ANSISTR_FLOAT'];
605   ss : shortstring;
606 begin
607   str_real(len,fr,d,treal_type(rt),ss);
608   s:=ss;
609 end;
612 Procedure AnsiStr_Cardinal(C : Cardinal;Len : Longint; Var S : AnsiString);[Public,Alias : 'FPC_ANSISTR_CARDINAL'];
614   SS : ShortString;
615 begin
616   int_str_cardinal(C,Len,SS);
617   S:=SS;
618 end;
622 Procedure AnsiStr_Longint(L : Longint; Len : Longint; Var S : AnsiString);[Public,Alias : 'FPC_ANSISTR_LONGINT'];
624   SS : ShortString;
625 begin
626   int_Str_Longint (L,Len,SS);
627   S:=SS;
628 end;
631 Procedure Delete (Var S : AnsiString; Index,Size: Longint);
633   LS : Longint;
634 begin
635   If Length(S)=0 then
636    exit;
637   if index<=0 then
638    begin
639      inc(Size,index-1);
640      index:=1;
641    end;
642   LS:=PAnsiRec(Pointer(S)-FirstOff)^.Len;
643   if (Index<=LS) and (Size>0) then
644    begin
645      UniqueString (S);
646      if Size+Index>LS then
647       Size:=LS-Index+1;
648      if Index+Size<=LS then
649       begin
650         Dec(Index);
651         Move(PByte(Pointer(S))[Index+Size],PByte(Pointer(S))[Index],LS-Index+1);
652       end;
653      Setlength(s,LS-Size);
654    end;
655 end;
658 Procedure Insert (Const Source : AnsiString; Var S : AnsiString; Index : Longint);
660   Temp : AnsiString;
661   LS : Longint;
662 begin
663   If Length(Source)=0 then
664    exit;
665   if index <= 0 then
666    index := 1;
667   Ls:=Length(S);
668   if index > LS then
669    index := LS+1;
670   Dec(Index);
671   Pointer(Temp) := NewAnsiString(Length(Source)+LS);
672   SetLength(Temp,Length(Source)+LS);
673   If Index>0 then
674     move (Pointer(S)^,Pointer(Temp)^,Index);
675   Move (Pointer(Source)^,PByte(Temp)[Index],Length(Source));
676   If (LS-Index)>0 then
677     Move(PByte(Pointer(S))[Index],PByte(temp)[Length(Source)+index],LS-Index);
678   S:=Temp;
679 end;
682 Function StringOfChar(c : char;l : longint) : AnsiString;
683 begin
684   SetLength(StringOfChar,l);
685   FillChar(Pointer(StringOfChar)^,Length(StringOfChar),c);
686 end;
688 Procedure SetString (Var S : AnsiString; Buf : PChar; Len : Longint);
690 begin
691   SetLength(S,Len);
692   Move (Buf[0],S[1],Len);
693 end;
696   $Log$
697   Revision 1.1  2002/02/19 08:25:20  sasu
698   Initial revision
700   Revision 1.1.2.7  2000/12/09 22:54:06  florian
701     * helper name for qword-val fixed
703   Revision 1.1.2.6  2000/12/09 22:37:31  florian
704     * fixed last commit, under go32v2 it worked
706   Revision 1.1.2.5  2000/12/08 13:46:36  jonas
707     + added pos(char,ansistring), because there is also a pos(char,shortstring)
708       and without the ansistring version, the shortstring version is always
709       called when calling pos(char,pchar), even when using $h+ (because the
710       first parameter matches exactly)
712   Revision 1.1.2.4  2000/08/29 18:37:38  peter
713     * fixed chararray to ansistring
715   Revision 1.1.2.3  2000/08/24 07:13:46  jonas
716     * fixed bug in setlength (it sometimes read after the end of the heap)
718   Revision 1.1.2.1  2000/08/10 18:53:35  peter
719     * int64 ansistring val support
721   Revision 1.1  2000/07/13 06:30:44  michael
722   + Initial import
724   Revision 1.43  2000/07/04 07:57:46  pierre
725    * Change Code to var param in ValAnsiUnsignedInt function
727   Revision 1.42  2000/06/11 07:02:30  peter
728     * UniqueAnsiString -> UniqueString for Delphi compatibility
730   Revision 1.41  2000/05/18 17:04:48  peter
731     * use freemem without size
733   Revision 1.40  2000/02/09 16:59:29  peter
734     * truncated log
736   Revision 1.39  2000/01/07 16:41:33  daniel
737     * copyright 2000
739   Revision 1.38  2000/01/07 16:32:24  daniel
740     * copyright 2000 added
742   Revision 1.37  1999/11/28 11:24:04  sg
743   * Fixed bug 722: If the start position of AnsiString Copy is less than 1,
744     it will be set to 1 (same behaviour as in Delphi)
746   Revision 1.36  1999/11/25 13:34:57  michael
747   + Added Ansistring setstring call
749   Revision 1.35  1999/11/06 14:35:38  peter
750     * truncated log
752   Revision 1.34  1999/11/02 23:57:54  peter
753     * fixed copy where size+index could be < 0
755   Revision 1.33  1999/10/27 14:27:49  florian
756     * StringOfChar fixed, how can be a bug in two lines of code ?????
758   Revision 1.32  1999/10/27 14:17:20  florian
759     + StringOfChar
761   Revision 1.31  1999/10/04 20:48:18  peter
762     * pos function speed up by a factor 40 :)