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.
40 TAnsiRec = Packed Record
48 AnsiRecLen = SizeOf(TAnsiRec);
49 FirstOff = SizeOf(TAnsiRec)-1;
52 {****************************************************************************
53 Internal functions, not in interface.
54 ****************************************************************************}
57 Procedure DumpAnsiRec(S : Pointer);
60 Writeln ('String is nil')
63 With PAnsiRec(S-Firstoff)^ do
65 Write ('(Maxlen: ',maxlen);
67 Writeln (' Ref: ',ref,')');
74 Function NewAnsiString(Len : Longint) : Pointer;
76 Allocate a new AnsiString on the heap.
77 initialize it to zero length and reference count 1.
82 { Also add +1 for a terminating zero }
83 GetMem(P,Len+AnsiRecLen);
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 }
96 Procedure DisposeAnsiString(Var S : Pointer);
98 Deallocates a AnsiString From the heap.
103 Dec (Longint(S),FirstOff);
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;
121 { check for constant strings ...}
122 l:=@PANSIREC(S-FirstOff)^.Ref;
126 { Ref count dropped to zero }
127 DisposeAnsiString (S); { Remove...}
128 { this pointer is not valid anymore, so set it to zero }
133 Procedure AnsiStr_Incr_Ref (Var S : Pointer);[Public,Alias:'FPC_ANSISTR_INCR_REF'];
137 { Let's be paranoid : Constant string ??}
138 If PAnsiRec(S-FirstOff)^.Ref<0 then exit;
139 Inc(PAnsiRec(S-FirstOff)^.Ref);
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.
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) }
158 Procedure AnsiStr_Concat (S1,S2 : Pointer;var S3 : Pointer);[Public, alias: 'FPC_ANSISTR_CONCAT'];
160 Concatenates 2 AnsiStrings : S1+S2.
164 Size,Location : Longint;
166 { create new result }
168 AnsiStr_Decr_Ref(S3);
169 { only assign if s1 or s2 is empty }
171 AnsiStr_Assign(S3,S2)
174 AnsiStr_Assign(S3,S1)
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);
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;
195 Location:=Length(S1);
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 }
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;
219 Size:=PAnsiRec(S2-FirstOff)^.Len;
220 If Size>high(S1) then
222 Move (S2^,S1[1],Size);
228 Procedure ShortStr_To_AnsiStr (Var S1 : Pointer; Const S2 : ShortString);[Public, alias: 'FPC_SHORTSTR_TO_ANSISTR'];
230 Converts a ShortString to a AnsiString;
236 Setlength (AnsiString(S1),Size);
239 Move (S2[1],Pointer(S1)^,Size);
241 PByte(Pointer(S1)+Size)^:=0;
246 Procedure Char_To_AnsiStr(var S1 : Pointer; c : Char);[Public, alias: 'FPC_CHAR_TO_ANSISTR'];
248 Converts a ShortString to a AnsiString;
251 Setlength (AnsiString(S1),1);
252 PByte(Pointer(S1))^:=byte(c);
254 PByte(Pointer(S1)+1)^:=0;
258 Procedure PChar_To_AnsiStr(var a : ansistring;p : pchar);[Public,Alias : 'FPC_PCHAR_TO_ANSISTR'];
262 if pointer(a)<>nil then
264 AnsiStr_Decr_Ref(Pointer(a));
267 if (not assigned(p)) or (p[0]=#0) Then
271 //!! Horribly inneficient, but I see no other way...
275 Pointer(a):=NewAnsistring(L);
277 Move (P[0],Pointer(A)^,L)
282 Procedure CharArray_To_AnsiStr(var a : ansistring;p : pchar;l:longint);[Public,Alias : 'FPC_CHARARRAY_TO_ANSISTR'];
291 { p[0] <> #0, checked above (JM) }
294 while (i<l) and (hp^<>#0) do
299 Pointer(a):=NewAnsistring(i);
301 Move (P[0],Pointer(A)^,i);
306 Function AnsiStr_Compare(S1,S2 : Pointer): Longint;[Public,Alias : 'FPC_ANSISTR_COMPARE'];
308 Compares 2 AnsiStrings;
315 i,MaxI,Temp : Longint;
318 Maxi:=Length(AnsiString(S1));
319 temp:=Length(AnsiString(S2));
323 While (i<MaxI) and (Temp=0) do
325 Temp:= PByte(S1+I)^ - PByte(S2+i)^;
329 temp:=Length(AnsiString(S1))-Length(AnsiString(S2));
330 AnsiStr_Compare:=Temp;
334 Procedure AnsiStr_CheckZero(p : pointer);[Public,Alias : 'FPC_ANSISTR_CHECKZERO'];
337 HandleErrorFrame(201,get_frame);
341 Procedure AnsiStr_CheckRange(len,index : longint);[Public,Alias : 'FPC_ANSISTR_RANGECHECK'];
343 if (index>len) or (Index<1) then
344 HandleErrorFrame(201,get_frame);
348 {$ifdef EXTRAANSISHORT}
349 Function AnsiStr_ShortStr_Compare (Var S1 : Pointer; Var S2 : ShortString): Longint;
351 Compares a AnsiString with a ShortString;
358 i,MaxI,Temp : Longint;
362 MaxI:=Length(AnsiString(S1));
363 if MaxI>byte(S2[0]) then
365 While (i<MaxI) and (Temp=0) do
367 Temp:= PByte(S1+I)^ - Byte(S2[i+1]);
370 AnsiStr_ShortStr_Compare:=Temp;
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;
385 If Pointer(S)=Nil then
388 Length:=PAnsiRec(Pointer(S)-FirstOff)^.Len;
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.
403 if Pointer(S)=nil then
405 { Need a complete new string...}
406 Pointer(s):=NewAnsiString(l);
409 If (PAnsiRec(Pointer(S)-FirstOff)^.Maxlen < L) or
410 (PAnsiRec(Pointer(S)-FirstOff)^.Ref <> 1) then
412 { Reallocation is needed... }
413 Temp:=Pointer(NewAnsiString(L));
416 if l < succ(length(s)) then
418 { also move terminating null }
419 else movelen := succ(length(s));
420 Move(Pointer(S)^,Temp^,movelen);
422 ansistr_decr_ref(Pointer(S));
425 { Force nil termination in case it gets shorter }
426 PByte(Pointer(S)+l)^:=0;
427 PAnsiRec(Pointer(S)-FirstOff)^.Len:=l;
432 if Pointer(S)<>nil then
433 ansistr_decr_ref (Pointer(S));
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.
447 If Pointer(S)=Nil then
449 if PAnsiRec(Pointer(S)-Firstoff)^.Ref<>1 then
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 }
460 Function Copy (Const S : AnsiString; Index,Size : Longint) : AnsiString;
462 ResultAddress : Pointer;
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;
477 ResultAddress:=Pointer(NewAnsiString (Size));
478 if ResultAddress<>Nil then
480 Move (Pointer(Pointer(S)+index)^,ResultAddress^,Size);
481 PAnsiRec(ResultAddress-FirstOff)^.Len:=Size;
482 PByte(ResultAddress+Size)^:=0;
485 Pointer(Copy):=ResultAddress;
489 Function Pos (Const Substr : AnsiString; Const Source : AnsiString) : Longint;
500 substrlen:=Length(SubStr);
501 maxi:=length(source)-substrlen;
503 while (e) and (i <= maxi) do
506 if Source[i]=SubStr[1] then
508 S:=copy(Source,i,substrlen);
510 if AnsiStr_Compare(se,Pointer(S))=0 then
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;
529 for i:=1 to length(s) do
539 Function ValAnsiFloat(Const S : AnsiString; Var Code : ValSInt): ValReal; [public, alias:'FPC_VAL_REAL_ANSISTR'];
543 AnsiStr_To_ShortStr(SS,Pointer(S));
544 ValAnsiFloat := ValFloat(SS,Code);
548 Function ValAnsiUnsignedInt (Const S : AnsiString; Var Code : ValSInt): ValUInt; [public, alias:'FPC_VAL_UINT_ANSISTR'];
552 AnsiStr_To_ShortStr(SS,Pointer(S));
553 ValAnsiUnsignedInt := ValUnsignedInt(SS,Code);
557 Function ValAnsiSignedInt (DestSize: longint; Const S : AnsiString; Var Code : ValSInt): ValSInt; [public, alias:'FPC_VAL_SINT_ANSISTR'];
561 AnsiStr_To_ShortStr (SS,Pointer(S));
562 ValAnsiSignedInt := ValSignedInt(DestSize,SS,Code);
565 Function ValAnsiUnsignedint64 (Const S : AnsiString; Var Code : ValSInt): qword; [public,alias:'FPC_VAL_QWORD_ANSISTR'];
569 if length(S)>255 then
573 AnsiStr_To_ShortStr(SS,Pointer(S));
574 ValAnsiUnsignedInt64 := ValQWord(SS,Code);
579 Function ValAnsiSignedInt64 (DestSize: longint; Const S : AnsiString; Var Code : ValSInt): Int64; [public, alias:'FPC_VAL_INT64_ANSISTR'];
583 if Length(S)>255 then
587 AnsiStr_To_ShortStr (SS,Pointer(S));
588 ValAnsiSignedInt64 := valInt64(SS,Code);
592 {$IfDef SUPPORT_FIXED}
593 Function ValAnsiFixed(Const S : AnsiString; Var Code : ValSint): ValReal; [public, alias:'FPC_VAL_FIXED_ANSISTR'];
597 AnsiStr_To_ShortStr (SS,Pointer(S));
598 ValAnsiFixed := Fixed(ValFloat(SS,Code));
600 {$EndIf SUPPORT_FIXED}
603 procedure AnsiStr_Float(d : ValReal;len,fr,rt : longint;var s : ansistring);[public,alias:'FPC_ANSISTR_FLOAT'];
607 str_real(len,fr,d,treal_type(rt),ss);
612 Procedure AnsiStr_Cardinal(C : Cardinal;Len : Longint; Var S : AnsiString);[Public,Alias : 'FPC_ANSISTR_CARDINAL'];
616 int_str_cardinal(C,Len,SS);
622 Procedure AnsiStr_Longint(L : Longint; Len : Longint; Var S : AnsiString);[Public,Alias : 'FPC_ANSISTR_LONGINT'];
626 int_Str_Longint (L,Len,SS);
631 Procedure Delete (Var S : AnsiString; Index,Size: Longint);
642 LS:=PAnsiRec(Pointer(S)-FirstOff)^.Len;
643 if (Index<=LS) and (Size>0) then
646 if Size+Index>LS then
648 if Index+Size<=LS then
651 Move(PByte(Pointer(S))[Index+Size],PByte(Pointer(S))[Index],LS-Index+1);
653 Setlength(s,LS-Size);
658 Procedure Insert (Const Source : AnsiString; Var S : AnsiString; Index : Longint);
663 If Length(Source)=0 then
671 Pointer(Temp) := NewAnsiString(Length(Source)+LS);
672 SetLength(Temp,Length(Source)+LS);
674 move (Pointer(S)^,Pointer(Temp)^,Index);
675 Move (Pointer(Source)^,PByte(Temp)[Index],Length(Source));
677 Move(PByte(Pointer(S))[Index],PByte(temp)[Length(Source)+index],LS-Index);
682 Function StringOfChar(c : char;l : longint) : AnsiString;
684 SetLength(StringOfChar,l);
685 FillChar(Pointer(StringOfChar)^,Length(StringOfChar),c);
688 Procedure SetString (Var S : AnsiString; Buf : PChar; Len : Longint);
692 Move (Buf[0],S[1],Len);
697 Revision 1.1 2002/02/19 08:25:20 sasu
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
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
736 Revision 1.39 2000/01/07 16:41:33 daniel
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
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
761 Revision 1.31 1999/10/04 20:48:18 peter
762 * pos function speed up by a factor 40 :)