1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 1992-2023, Free Software Foundation, Inc. --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
24 ------------------------------------------------------------------------------
27 with Output
; use Output
;
30 package body Stringt
is
32 -- The following table stores the sequence of character codes for the
33 -- stored string constants. The entries are referenced from the
34 -- separate Strings table.
36 package String_Chars
is new Table
.Table
(
37 Table_Component_Type
=> Char_Code
,
38 Table_Index_Type
=> Int
,
40 Table_Initial
=> Alloc
.String_Chars_Initial
,
41 Table_Increment
=> Alloc
.String_Chars_Increment
,
42 Table_Name
=> "String_Chars");
44 -- The String_Id values reference entries in the Strings table, which
45 -- contains String_Entry records that record the length of each stored
46 -- string and its starting location in the String_Chars table.
48 type String_Entry
is record
53 package Strings
is new Table
.Table
(
54 Table_Component_Type
=> String_Entry
,
55 Table_Index_Type
=> String_Id
'Base,
56 Table_Low_Bound
=> First_String_Id
,
57 Table_Initial
=> Alloc
.Strings_Initial
,
58 Table_Increment
=> Alloc
.Strings_Increment
,
59 Table_Name
=> "Strings");
61 -- Note: it is possible that two entries in the Strings table can share
62 -- string data in the String_Chars table, and in particular this happens
63 -- when Start_String is called with a parameter that is the last string
64 -- currently allocated in the table.
66 Strings_Last
: String_Id
:= First_String_Id
;
67 String_Chars_Last
: Int
:= 0;
68 -- Strings_Last and String_Chars_Last are used by procedure Mark and
69 -- Release to get a snapshot of the tables and to restore them to their
70 -- previous situation.
76 procedure Append
(Buf
: in out Bounded_String
; S
: String_Id
) is
78 for X
in 1 .. String_Length
(S
) loop
79 Append
(Buf
, Get_Character
(Get_String_Char
(S
, X
)));
87 function End_String
return String_Id
is
96 function Get_String_Char
(Id
: String_Id
; Index
: Int
) return Char_Code
is
98 pragma Assert
(Id
in First_String_Id
.. Strings
.Last
99 and then Index
in 1 .. Strings
.Table
(Id
).Length
);
101 return String_Chars
.Table
(Strings
.Table
(Id
).String_Index
+ Index
- 1);
108 procedure Initialize
is
113 -- Set up the null string
116 Null_String_Id
:= End_String
;
125 String_Chars
.Release
;
126 String_Chars
.Locked
:= True;
128 Strings
.Locked
:= True;
137 Strings_Last
:= Strings
.Last
;
138 String_Chars_Last
:= String_Chars
.Last
;
147 Strings
.Set_Last
(Strings_Last
);
148 String_Chars
.Set_Last
(String_Chars_Last
);
155 -- Version to start completely new string
157 procedure Start_String
is
159 Strings
.Append
((String_Index
=> String_Chars
.Last
+ 1, Length
=> 0));
162 -- Version to start from initially stored string
164 procedure Start_String
(S
: String_Id
) is
166 Strings
.Increment_Last
;
168 -- Case of initial string value is at the end of the string characters
169 -- table, so it does not need copying, instead it can be shared.
171 if Strings
.Table
(S
).String_Index
+ Strings
.Table
(S
).Length
=
172 String_Chars
.Last
+ 1
174 Strings
.Table
(Strings
.Last
).String_Index
:=
175 Strings
.Table
(S
).String_Index
;
177 -- Case of initial string value must be copied to new string
180 Strings
.Table
(Strings
.Last
).String_Index
:=
181 String_Chars
.Last
+ 1;
183 for J
in 1 .. Strings
.Table
(S
).Length
loop
185 (String_Chars
.Table
(Strings
.Table
(S
).String_Index
+ (J
- 1)));
189 -- In either case the result string length is copied from the argument
191 Strings
.Table
(Strings
.Last
).Length
:= Strings
.Table
(S
).Length
;
194 -----------------------
195 -- Store_String_Char --
196 -----------------------
198 procedure Store_String_Char
(C
: Char_Code
) is
200 String_Chars
.Append
(C
);
201 Strings
.Table
(Strings
.Last
).Length
:=
202 Strings
.Table
(Strings
.Last
).Length
+ 1;
203 end Store_String_Char
;
205 procedure Store_String_Char
(C
: Character) is
207 Store_String_Char
(Get_Char_Code
(C
));
208 end Store_String_Char
;
210 ------------------------
211 -- Store_String_Chars --
212 ------------------------
214 procedure Store_String_Chars
(S
: String) is
216 for J
in S
'First .. S
'Last loop
217 Store_String_Char
(Get_Char_Code
(S
(J
)));
219 end Store_String_Chars
;
221 procedure Store_String_Chars
(S
: String_Id
) is
223 -- We are essentially doing this:
225 -- for J in 1 .. String_Length (S) loop
226 -- Store_String_Char (Get_String_Char (S, J));
229 -- but when the string is long it's more efficient to grow the
230 -- String_Chars table all at once.
232 S_First
: constant Int
:= Strings
.Table
(S
).String_Index
;
233 S_Len
: constant Nat
:= String_Length
(S
);
234 Old_Last
: constant Int
:= String_Chars
.Last
;
235 New_Last
: constant Int
:= Old_Last
+ S_Len
;
238 String_Chars
.Set_Last
(New_Last
);
239 String_Chars
.Table
(Old_Last
+ 1 .. New_Last
) :=
240 String_Chars
.Table
(S_First
.. S_First
+ S_Len
- 1);
241 Strings
.Table
(Strings
.Last
).Length
:=
242 Strings
.Table
(Strings
.Last
).Length
+ S_Len
;
243 end Store_String_Chars
;
245 ----------------------
246 -- Store_String_Int --
247 ----------------------
249 procedure Store_String_Int
(N
: Int
) is
252 Store_String_Char
('-');
253 Store_String_Int
(-N
);
257 Store_String_Int
(N
/ 10);
260 Store_String_Char
(Character'Val (Character'Pos ('0') + N
mod 10));
262 end Store_String_Int
;
264 --------------------------
265 -- String_Chars_Address --
266 --------------------------
268 function String_Chars_Address
return System
.Address
is
270 return String_Chars
.Table
(0)'Address;
271 end String_Chars_Address
;
277 function String_Equal
(L
, R
: String_Id
) return Boolean is
278 Len
: constant Nat
:= Strings
.Table
(L
).Length
;
281 if Len
/= Strings
.Table
(R
).Length
then
284 for J
in 1 .. Len
loop
285 if Get_String_Char
(L
, J
) /= Get_String_Char
(R
, J
) then
294 -----------------------------
295 -- String_From_Name_Buffer --
296 -----------------------------
298 function String_From_Name_Buffer
299 (Buf
: Bounded_String
:= Global_Name_Buffer
) return String_Id
303 Store_String_Chars
(+Buf
);
305 end String_From_Name_Buffer
;
311 function String_Length
(Id
: String_Id
) return Nat
is
313 return Strings
.Table
(Id
).Length
;
320 function String_To_Name
(S
: String_Id
) return Name_Id
is
321 Buf
: Bounded_String
;
324 return Name_Find
(Buf
);
327 ---------------------------
328 -- String_To_Name_Buffer --
329 ---------------------------
331 procedure String_To_Name_Buffer
(S
: String_Id
) is
334 Append
(Global_Name_Buffer
, S
);
335 end String_To_Name_Buffer
;
337 ---------------------
338 -- Strings_Address --
339 ---------------------
341 function Strings_Address
return System
.Address
is
343 return Strings
.Table
(First_String_Id
)'Address;
350 function To_String
(S
: String_Id
) return String is
351 Buf
: Bounded_String
;
354 return To_String
(Buf
);
363 String_Chars
.Locked
:= False;
364 Strings
.Locked
:= False;
367 -------------------------
368 -- Unstore_String_Char --
369 -------------------------
371 procedure Unstore_String_Char
is
373 String_Chars
.Decrement_Last
;
374 Strings
.Table
(Strings
.Last
).Length
:=
375 Strings
.Table
(Strings
.Last
).Length
- 1;
376 end Unstore_String_Char
;
378 ---------------------
379 -- Write_Char_Code --
380 ---------------------
382 procedure Write_Char_Code
(Code
: Char_Code
) is
384 procedure Write_Hex_Byte
(J
: Char_Code
);
385 -- Write single hex byte (value in range 0 .. 255) as two digits
391 procedure Write_Hex_Byte
(J
: Char_Code
) is
392 Hexd
: constant array (Char_Code
range 0 .. 15) of Character :=
395 Write_Char
(Hexd
(J
/ 16));
396 Write_Char
(Hexd
(J
mod 16));
399 -- Start of processing for Write_Char_Code
402 if Code
in 16#
20#
.. 16#
7E#
then
403 Write_Char
(Character'Val (Code
));
409 if Code
> 16#FF_FFFF#
then
410 Write_Hex_Byte
(Code
/ 2 ** 24);
413 if Code
> 16#FFFF#
then
414 Write_Hex_Byte
((Code
/ 2 ** 16) mod 256);
417 if Code
> 16#FF#
then
418 Write_Hex_Byte
((Code
/ 256) mod 256);
421 Write_Hex_Byte
(Code
mod 256);
427 ------------------------------
428 -- Write_String_Table_Entry --
429 ------------------------------
431 procedure Write_String_Table_Entry
(Id
: String_Id
) is
435 if Id
= No_String
then
436 Write_Str
("no string");
441 for J
in 1 .. String_Length
(Id
) loop
442 C
:= Get_String_Char
(Id
, J
);
444 if C
= Get_Char_Code
('"') then
450 -- If string is very long, quit
452 if J
>= 1000 then -- arbitrary limit
453 Write_Str
("""...etc (length = ");
454 Write_Int
(String_Length
(Id
));
462 end Write_String_Table_Entry
;